boost :: variant的访客模板

Tom*_*dan 10 c++ templates boost variant boost-variant

我想用一个boost.variant<T0,T1,T2>作为参数传递到模板"访问者"类,它会为游客提供运营商所要求的boost.variant游客机制,在这种情况下,所有返回void即

void operator()(T0 value);
void operator()(T1 value);
void operator()(T2 value);
Run Code Online (Sandbox Code Playgroud)

该模板还将为变体中的每个类型T0 ...具有相应的虚函数,该函数默认不执行任何操作.用户可以从模板类继承并仅重新定义他感兴趣的虚拟函数.这类似于众所周知的"模板方法"模式.我能够想出的唯一解决方案是将boost :: variant和关联的访问者包装在一个模板中,并通过typedef访问它们.这没关系,但感觉有点笨重.这是代码:

#include "boost/variant.hpp"

//create specializations of VariantWrapper for different numbers of variants - 
//just show a template for a variant with three types here. 
//variadic template parameter list would be even better! 

template<typename T0, typename T1, typename T2>
struct VariantWrapper
{
    //the type for the variant
    typedef boost::variant<T0,T1,T2> VariantType;

    //The visitor class for this variant
    struct Visitor : public boost::static_visitor<>
    {
        void operator()(T0 value)
        {
            Process(value);
        }
        void operator()(T1 value)
        {
            Process(value);
        }
        void operator()(T2 value)
        {
            Process(value);
        }
        virtual void Process(T0 val){/*do nothing */}
        virtual void Process(T1 val){/*do nothing */}
        virtual void Process(T2 val){/*do nothing */}
    protected:
        Visitor(){}
    };

    typedef Visitor VisitorType;
private:
    VariantWrapper(){}
    };
Run Code Online (Sandbox Code Playgroud)

然后按如下方式使用该类:

typedef VariantWapper<bool,int,double> VariantWrapperType;
typedef VariantWrapperType::VariantType VariantType;
typedef VariantWrapperType::VisitorType VisitorType;

struct Visitor : public VisitorType
{
    void Process(bool val){/*do something*/}
    void Process(int val){/*do something*/}
    /* this class is not interested in the double value */
};

VariantType data(true);
apply_visitor(Visitor(),data);
Run Code Online (Sandbox Code Playgroud)

正如我所说,这似乎工作正常但我更喜欢它,如果我不必创建一个特殊的包装类来将变量和访问者联系在一起.我希望能够直接使用boost.variant来实例化模板访问者类.我已经看过使用类型参数,非类型参数和模板模板参数,但似乎没有任何建议.我想做的不可能吗?我可能会遗漏一些东西,如果有人对此有任何意见,我会很感激.

小智 15

使用Boost Variant和虚拟调度的代码有点可疑.特别是考虑到您在编译期间知道您对处理感兴趣的内容,并且完全没有必要在运行时创建虚拟表以实现您的目标.

我建议你使用部分模板专业化.因此,有一个默认的模板方法,可以接受变体中的任何类型,并且什么都不做.对于您感兴趣的那些类型,只需专门化模板.

这是一个例子.我们有三种类型 - Foo,Bar和War.我们只对最后两种类型感兴趣,并对它们有专业化.所以Foo被忽略了.

#include <iostream>
#include <boost/variant.hpp>

using namespace std;
using namespace boost;

struct Foo {};
struct Bar {};
struct War {};

typedef variant<Foo, Bar, War> Guess;

struct Guesstimator : public boost::static_visitor<void>
{
    template <typename T>
    void operator () (T) const
    {
    }
};

template <>
inline void
Guesstimator::operator () <Bar> (Bar) const
{
    cout << "Let's go to a pub!" << endl;
}

template <>
inline void
Guesstimator::operator () <War> (War) const
{
    cout << "Make love, not war!" << endl;
}
Run Code Online (Sandbox Code Playgroud)

这是一个简单的用法示例:

int
main ()
{
    Guess monday;
    apply_visitor (Guesstimator (), monday);

    War war;
    Guess ww2 (war);
    apply_visitor (Guesstimator (), ww2);

    Bar irishPub;
    Guess friday (irishPub);
    apply_visitor (Guesstimator (), friday);
}
Run Code Online (Sandbox Code Playgroud)

该计划的输出将是:

Make love, not war!
Let's go to a pub!
Run Code Online (Sandbox Code Playgroud)

这是另一种解决方案.我们创建一个默认访问者,忽略所有内容,除了您在类型列表中指定的内容.这不方便,因为您必须指定两次类型列表 - 一次在类型列表中,然后在每个处理方法(运算符)中.此外,通用模板实际上将继承您的访问者.但是,我们走了:

#include <cstddef>
#include <iostream>
#include <boost/variant.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/contains.hpp>
#include <boost/utility/enable_if.hpp>

// Generic visitor that does magical dispatching of
// types and delegates passes down to your visitor only
// those types specified in a type list.
template <typename Visitor, typename TypeList>
struct picky_visitor :
    public boost::static_visitor<void>,
    public Visitor
{
    template <typename T>
    inline void
    operator () (T v, typename boost::enable_if< typename boost::mpl::contains< TypeList, T >::type >::type *dummy = NULL) const
    {
        Visitor::operator () (v);
    }

    template <typename T>
    inline void
    operator () (T v, typename boost::disable_if<typename boost::mpl::contains< TypeList, T >::type >::type *dummy = NULL) const
    {
    }
};

// Usage example:

struct nil {};
typedef boost::variant<nil, char, int, double> sql_field;

struct example_visitor
{
    typedef picky_visitor< example_visitor, boost::mpl::vector<char, int> > value_type;

    inline void operator () (char v) const
    {
        std::cout << "character detected" << std::endl;
    }

    inline void operator () (int v) const
    {
        std::cout << "integer detected" << std::endl;
    }
};

int
main ()
{
    example_visitor::value_type visitor;

    sql_field nilField;
    sql_field charField ('X');
    sql_field intField (1986);
    sql_field doubleField (19.86);

    boost::apply_visitor (visitor, nilField);
    boost::apply_visitor (visitor, charField);
    boost::apply_visitor (visitor, intField);
    boost::apply_visitor (visitor, doubleField);
}
Run Code Online (Sandbox Code Playgroud)