避免boost :: variant访问者

时间:2016-09-23 10:32:36

标签: c++ boost boost-variant apply-visitor

我遇到以下问题: 我有一些访问者为boost :: variant,它们对特定类型都做同样的事情,这里是foo,所以方法

void operator()(const foo& ast)
{
    //allways the same
}

在每个访客中都是一样的。 由于我不想在所有访问者中编写这种冗余方法,因此我试图避免为所有访问者添加一个实现此方法的公共基类。 问题该方法递归调用访问者本身,如下所示:

void operator(const foo& ast)
{
    for(auto&& item : ast.members)
    {
        boost::apply_visitor(*this, item);
    }
}

并且因为所有其他匹配成员的方法都没有在基类中实现,所以我得到了一个编译器错误。 现在我的问题是,如何摆脱冗余代码?

以下是问题的示例:

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

1 个答案:

答案 0 :(得分:2)

这样的东西?

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

或者访问者中使用的类型是预先相同/已知的,虚函数是正常的:

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派生类型,从而导致未定义的行为。所以要小心。