0

私は次の問題に直面しています:私はboost::variantのために何人かの訪問者を持っています。

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

1 に答える 1

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未定義の動作につながります。ので注意してください。

于 2016-09-23T10:45:41.833 に答える