【问题标题】:Templating and Vistor pattern模板和访客模式
【发布时间】:2012-08-14 13:15:04
【问题描述】:

我有一些带有访问功能的类,如下所示:

struct Person {
   std::string name;
   unsigned age;

  template<class Visitor>
  void visit(Visitor& c)
  {
      c("name", name);
      c("age", age);
  }

  template<class Visitor>
  void visit(Visitor& c) const
  {
      c("name", name);
      c("age", age);
  }
};

我有这样的访客:

struct PrintVisitor
{
    PrintVisitor(std::ostream& stream)
        : m_stream(stream)
    {}

    template<class T>
    void operator()(const std::string& name, const T& v)
    {
        m_stream << name << ": " << v << std::endl;
    }

private:
    std::ostream& m_stream;
};

我想为每个访问者定义一个流操作符:

std::ostream& operator<<(std::ostream& stream, const Person& p)
{
    PrintVisitor printer(stream);
    p.visit(printer);
    return stream;
}

是否可以提供一个接受任何 Visitable 类的操作符

(现在,我只是在尝试打印,但实际上我想实现 json 序列化、反序列化以及可能的相等和小于运算符。

更新

我使用了 Davids 解决方案:

CRTP 的基类:

template <class T>
struct Visitable {};

所有可访问的类都从那里继承:

struct Person : Visitable<Person> { ... }

操作符和函数都是这样模板化的,并使用静态转换来访问类:

template<class T> 
std::ostream& operator<<(std::ostream& stream, const Visitable<T>& p) 
{ 
    PrintVisitor printer(stream); 
    static_cast<const T&>(p).visit(printer); 
    return stream; 
}

【问题讨论】:

  • 我在解析“是否有可能不必在模板函数中只实现一次运算符”时遇到困难

标签: c++ templates boost c++11


【解决方案1】:

您采用的方法类似于 boost 序列化库的实现方式,不同之处在于它们重载 operator&amp;(二进制而不是地址)以与库交互。然后访问者将对类型使用单个serialize 操作。

现在,问题是我不太明白这个问题:

是否有可能不必在模板函数中只实现一次运算符?

您的意思是提供一个operator&lt;&lt; 可以接受任何访问者(序列化器)类型吗?如果这是问题所在,并且无需详细说明,您可以尝试使用继承和 CRTP 来解决此问题。将运算符实现为所有访问者的模板库中的成员函数,并将具体访问者作为参数。

我不太确定这将如何应用于比较运算符,虽然 operator&lt;&lt;operator&gt;&gt; 对于序列化看起来很自然,但对于任何其他操作它们都会令人惊讶。

【讨论】:

  • 所以我有一个基本模板类,它实现了我希望访问者拥有的所有操作/运算符。谢谢
  • @njr: Google for CRTP,它不是创建一个基类那么简单,要能够执行你想要的代码,你需要一些模板黑客。
  • 谢谢,一切正常,有趣的是我的基类定义是空的。
  • 既然我已经让它工作了,我应该发布我的代码作为答案吗?
  • @njr:将其作为答案发布(或者作为对问题的编辑,但要明确区分它是答案)。空荡荡的基地……听起来有点奇怪……
【解决方案2】:

你的意思是你想做这样的事情吗?

template<class Visitable>
std::ostream& operator<<(std::ostream& stream, const Visitable& v)
{
    PrintVisitor printer(stream);
    v.visit(printer);
    return stream;
}

这当然会引起歧义。你想要的是这样的:

template<class Visitable implements visit>
std::ostream& operator<<(std::ostream& stream, const Visitable& v)
{
    PrintVisitor printer(stream);
    v.visit(printer);
    return stream;
}

我不知道这是否可能。

您也可以这样做:

class Visitable { ... }
class Person : public Visitable { ... }

然后实现一个正常的功能:

std::ostream& operator<<(std::ostream& stream, const Visitable& v)

但是你有一个虚函数调用的小开销。

【讨论】:

  • 这种方法的问题在于,这种模板化重载的存在会导致标准中提供的一些operator&lt;&lt;s 不明确。
  • 是的,我知道,我只是在澄清您要做什么。您希望仅在 Visitable 实现了访问功能时才实例化此模板。我不清楚这是否可以通过模板专业化、SFINAE 等来实现。 (显然可以通过继承来完成,你可以创建一个基类Visitible,然后实现一个普通的非模板函数)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-01-18
  • 2011-02-06
  • 1970-01-01
  • 2012-04-30
  • 1970-01-01
  • 2012-03-10
  • 1970-01-01
相关资源
最近更新 更多