模板和访客模式

时间:2012-08-14 13:15:04

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

我有一些带有访问函数的类,如下所示:

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;
}

是否可以提供单个运营商&lt;&lt;接受任何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; 
}

2 个答案:

答案 0 :(得分:1)

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

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

  

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

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

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

答案 1 :(得分:0)

你是说你想做这样的事吗?

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)

但是你的虚拟函数调用开销很小。