避免 boost::variant 访问者中的冗余代码
avoiding redundant code in boost::variant visitors
我面临以下问题:
我有一些访问者在访问 boost::variant,它们都对特定类型做同样的事情,这里是 foo,所以方法
void operator()(const foo& ast)
{
//allways the same
}
对每一位访客来说都是一样的。
由于我不想在所有访问者中编写这个冗余方法,因此我试图避免向所有访问者添加一个实现此方法的公共基础class。
问题方法递归调用访问者本身,如下所示:
void operator(const foo& ast)
{
for(auto&& item : ast.members)
{
boost::apply_visitor(*this, item);
}
}
并且由于所有其他与成员匹配的方法都没有在基础 class 中实现,因此我得到一个编译器错误。
现在我的问题是,我怎样才能摆脱我的冗余代码?
这里是一个关于问题可能的例子:
struct variant_one;
struct variant_two;
struct nil{};
typedef boost::variant<
boost::spirit::x3::forward_ast<variant_one>,
boost::spirit::x3::forward_ast<variant_two>,
nil
> example_variant;
struct variant_one {};
struct variant_two
{
std::vector<example_variant> members;
};
struct visitor_one : boost::static_visitor<void>
{
void operator()(const variant_one& ast)
{
std::cout << "visitor_one detected var_one" << std::endl;
}
//this is the redundant method
void operator()(const variant_two& ast)
{
std::cout << "visitor detected var_two, output members:" <<std::endl;
for(auto&& member : ast.members)
{
boost::apply_visitor(*this, member);
}
}
}
struct visitor_two : boost::static_visitor<void>
{
void operator()(const variant_one& ast)
{
std::cout << "visitor_one detected var_two" << std::endl;
}
//this is the redundant method
void operator()(const variant_two& ast)
{
std::cout << "visitor detected var_two, output members:" <<std::endl;
for(auto&& member : ast.members)
{
boost::apply_visitor(*this, member);
}
}
}
是这样的吗?
template<typename Derived>
struct VisitorBase {
void operator()(const foo& ast) {
for(auto&& item : ast.members) {
boost::apply_visitor(*static_cast<Derived*>(this), item);
}
}
};
struct VisitorA : VisitorBase<VisitorA> {
void operator()(const ItemType& item) {
// do stuff
}
};
或者如果访问者使用的类型是same/known并且虚函数是好的:
struct VisitorBase {
void operator()(const foo& ast) {
for(auto&& item : ast.members) {
boost::apply_visitor(*this, item);
}
}
virtual void operator()(const ItemTypeA&) = 0;
virtual void opetator()(const ItemTypeB&) = 0;
};
struct VisitorA : VisitorBase {
void operator()(const ItemTypeA& item) {
// do stuff
}
void operator()(const ItemTypeB& item) {
// do stuff
}
};
在第一个示例中,您可能希望确保不会意外地使用非派生类型实例化模板,例如:
static_assert(std::is_base_of<VisitorBase,Derived>::value, "Derived should be derived from VisitorBase");
这仍然会保留在模板参数中使用不同的 VisitorBase
派生类型实例化 VisitorBase
派生类型的可能性,从而导致未定义的行为。所以要小心。
我面临以下问题: 我有一些访问者在访问 boost::variant,它们都对特定类型做同样的事情,这里是 foo,所以方法
void operator()(const foo& ast)
{
//allways the same
}
对每一位访客来说都是一样的。 由于我不想在所有访问者中编写这个冗余方法,因此我试图避免向所有访问者添加一个实现此方法的公共基础class。 问题方法递归调用访问者本身,如下所示:
void operator(const foo& ast)
{
for(auto&& item : ast.members)
{
boost::apply_visitor(*this, item);
}
}
并且由于所有其他与成员匹配的方法都没有在基础 class 中实现,因此我得到一个编译器错误。 现在我的问题是,我怎样才能摆脱我的冗余代码?
这里是一个关于问题可能的例子:
struct variant_one;
struct variant_two;
struct nil{};
typedef boost::variant<
boost::spirit::x3::forward_ast<variant_one>,
boost::spirit::x3::forward_ast<variant_two>,
nil
> example_variant;
struct variant_one {};
struct variant_two
{
std::vector<example_variant> members;
};
struct visitor_one : boost::static_visitor<void>
{
void operator()(const variant_one& ast)
{
std::cout << "visitor_one detected var_one" << std::endl;
}
//this is the redundant method
void operator()(const variant_two& ast)
{
std::cout << "visitor detected var_two, output members:" <<std::endl;
for(auto&& member : ast.members)
{
boost::apply_visitor(*this, member);
}
}
}
struct visitor_two : boost::static_visitor<void>
{
void operator()(const variant_one& ast)
{
std::cout << "visitor_one detected var_two" << std::endl;
}
//this is the redundant method
void operator()(const variant_two& ast)
{
std::cout << "visitor detected var_two, output members:" <<std::endl;
for(auto&& member : ast.members)
{
boost::apply_visitor(*this, member);
}
}
}
是这样的吗?
template<typename Derived>
struct VisitorBase {
void operator()(const foo& ast) {
for(auto&& item : ast.members) {
boost::apply_visitor(*static_cast<Derived*>(this), item);
}
}
};
struct VisitorA : VisitorBase<VisitorA> {
void operator()(const ItemType& item) {
// do stuff
}
};
或者如果访问者使用的类型是same/known并且虚函数是好的:
struct VisitorBase {
void operator()(const foo& ast) {
for(auto&& item : ast.members) {
boost::apply_visitor(*this, item);
}
}
virtual void operator()(const ItemTypeA&) = 0;
virtual void opetator()(const ItemTypeB&) = 0;
};
struct VisitorA : VisitorBase {
void operator()(const ItemTypeA& item) {
// do stuff
}
void operator()(const ItemTypeB& item) {
// do stuff
}
};
在第一个示例中,您可能希望确保不会意外地使用非派生类型实例化模板,例如:
static_assert(std::is_base_of<VisitorBase,Derived>::value, "Derived should be derived from VisitorBase");
这仍然会保留在模板参数中使用不同的 VisitorBase
派生类型实例化 VisitorBase
派生类型的可能性,从而导致未定义的行为。所以要小心。