我有一些带有访问函数的类,如下所示:
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;
}
答案 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)
但是你的虚拟函数调用开销很小。