【问题标题】:Factory returning boost::visitor objects工厂返回 boost::visitor 对象
【发布时间】:2012-11-19 13:48:38
【问题描述】:

我希望创建一个访问者工厂,它返回以下任一类型的对象:

class Visitor1: public boost::static_visitor<int>
{
public:
    int operator()(int& ) const 
    {
        return 1;
    }

    int operator()(bool& ) const 
    {
        return 1;
    }
};

class Visitor2: public boost::static_visitor<int>
{
public:
    int operator()(int& ) const 
    {
        return 2;
    }

    int operator()(bool& ) const 
    {
        return 2;
    }
};

我假设我可以为工厂创建的对象返回一个 boost::static_visitor* 类型的指针,但这不会编译。我也无法引用该对象,如下所示:

Visitor1 v;
Type t;
boost::static_visitor<int>& vR = v;
boost::apply_visitor(vR, t);

我得到的错误是:

see reference to function template instantiation 'int boost::apply_visitor<boost::static_visitor<R>,Type>(Visitor &,Visitable &)' being compiled
1>          with
1>          [
1>              R=int,
1>              Visitor=boost::static_visitor<int>,
1>              Visitable=Type
1>          ]
1>c:\boost_1_49_0\boost\variant\variant.hpp(858): error C2064: term does not evaluate to a function taking 1 arguments 

我应该如何实现我的工厂?

【问题讨论】:

    标签: c++ boost c++98


    【解决方案1】:

    访问者使用重载,这意味着它们是静态组件。工厂总是(?)意味着运行时组件。首先,重要的是确定所有派生访问者是否共享同一组重载。如果是这样,您可以使用此方案:

    #include <memory>
    
    #include <boost/variant.hpp>
    #include <boost/variant/static_visitor.hpp>
    
    struct visitor_base : public boost::static_visitor<int>
    {
      virtual int operator()(int) = 0;
      virtual int operator()(double) = 0;
      virtual int operator()(const char*) = 0;
      virtual ~visitor_base() {}
    };
    
    struct impl_1 : visitor_base {
      virtual int operator()(int) { return 1; }
      virtual int operator()(double) { return 1; }
      virtual int operator()(const char*) { return 1; }
    };
    
    struct impl_2 : visitor_base {
      virtual int operator()(int) { return 2; }
      virtual int operator()(double) { return 2; }
      virtual int operator()(const char*) { return 2; }
    };
    
    std::unique_ptr<visitor_base> visitor_factory(int i) {
      if(i == 1) {
        return std::unique_ptr<visitor_base>(new impl_1);
      } else {
        return std::unique_ptr<visitor_base>(new impl_2);
      }
    }
    
    int main()
    {
      auto vis = visitor_factory(1);
      boost::variant<int, double, const char*> v = 3;
      std::cout << boost::apply_visitor(*vis, v) << std::endl;
      auto vis2 = visitor_factory(2);
      std::cout << boost::apply_visitor(*vis2, v) << std::endl;
    
      return 0;
    }
    

    如果您的派生访问者仅适用于变体中可能类型的子集,则您不能使用纯虚函数,并且需要某种机制来报告未能处理基中的值(例如异常)。

    当然,示例代码中使用的少数 C++11 特性很容易替换。

    #include <memory>
    #include <string>
    
    #include <boost/variant.hpp>
    #include <boost/variant/static_visitor.hpp>
    
    struct visitor_base : public boost::static_visitor<int>
    {
      // the catch all
      template <typename T>
      int operator()(const T& t) {
        return 0;
      }
    
      virtual int operator()(int) {}
      virtual int operator()(double) {}
      virtual int operator()(const char*) {}
    
      virtual ~visitor_base() {}
    };
    
    struct impl_1 : visitor_base {
      virtual int operator()(int) { return 1; }
      virtual int operator()(double) { return 1; }
      virtual int operator()(const char*) { return 1; }
    };
    
    struct impl_2 : visitor_base {
      virtual int operator()(int) { return 2; }
      virtual int operator()(double) { return 2; }
      virtual int operator()(const char*) { return 2; }
    };
    
    std::unique_ptr<visitor_base> visitor_factory(int i) {
      if(i == 1) {
        return std::unique_ptr<visitor_base>(new impl_1);
      } else {
        return std::unique_ptr<visitor_base>(new impl_2);
      }
    }
    
    int main()
    {
      auto vis = visitor_factory(1);
      boost::variant<int, double, const char*> v = 3;
      std::cout << boost::apply_visitor(*vis, v) << std::endl;
      auto vis2 = visitor_factory(2);
      std::cout << boost::apply_visitor(*vis2, v) << std::endl;
    
      // a variant with more than impl_1 and impl_2 can catch
      boost::variant<int, double, const char*, std::string> vv = std::string{"asdf"};
      std::cout << boost::apply_visitor(*vis2, vv) << std::endl;
      // no use one that we handle
      vv = 3;
      std::cout << boost::apply_visitor(*vis2, vv) << std::endl;
      return 0;
    }
    

    【讨论】:

    • 谢谢!我还留下了一个接口可以用来解决这个问题。但是,我只适用于您提到的可能类型的子集。我曾计划使用模板来捕获与特定访问者实现无关的所有类型,但您不能混合使用虚拟和模板。我该如何解决这个问题,或者我是否需要为与特定访问者实现无关的每种类型调用断言。
    • 你的意思是我应该把基类中的每一个纯虚函数都替换成一个会抛出异常的虚函数吗?
    • @Baz 虽然您无法调用模板化的operator() 虚拟网络,但您可以使用using visitor_base::operator() 将其纳入派生访问者的范围内。
    • @pnr 你介意编辑你的答案来用一个简单的例子来证明这一点吗?谢谢!
    • @Baz 我添加了第二个示例,该示例显示了全部情况。这里很大程度上取决于您的确切用例。是否所有派生函子都覆盖相同的重载,或者只有部分重叠等。它可能变得相当脆弱。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-10
    • 2021-11-22
    • 1970-01-01
    相关资源
    最近更新 更多