【问题标题】:How to pass `boost::static_visitor` instances to functions如何将`boost::static_visitor`实例传递给函数
【发布时间】:2017-04-28 09:20:50
【问题描述】:

我在我的项目中经常使用boost::variant。我的同事现在提出了传递特定 boost::static_visitor<int> 实例的想法,以便自定义访问类型。她有一些类似下面的代码:

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

typedef boost::variant<int, std::string> TVar;

struct Visitor1 : public boost::static_visitor<int> {
    template<typename T> 
    result_type operator()(const T&) {
        return 42;
    }
};

struct Visitor2 : public boost::static_visitor<int> {
    template<typename T>
    result_type operator()(const T&) {
        return 21;
    }
};

 int adder(const boost::static_visitor<int>& visitor, const TVar& visitable) {
     return visitable.apply_visitor(visitor) + 1;
 }

int main(int argc, char **args) {
    Visitor1 v1;
    Visitor2 v2;
    TVar x;

    std::cout << adder(v1, x) << std::endl;
    std::cout << adder(v2, x) << std::endl;
}

在我看来,它听起来很完美,但它无法编译。我的编译器说 expression will not yield a function that takes one 1 argument 某处内幕。

我们做错了什么?

【问题讨论】:

    标签: c++ templates boost visitor-pattern boost-variant


    【解决方案1】:

    apply_visitor 有一个重载,它只接收访问者。我猜这会返回一个适合您需求的部分应用函数对象:

    这个重载返回一个

    Class template apply_visitor_delayed_t boost::apply_visitor_delayed_t — Adapts a visitor for use as a function object.

    就个人而言,我喜欢像这样向访问者对象添加重载:

    struct MyVisitor {
        typedef void result_type;
    
        template <typename... Ts>
        result_type operator()(boost::variant<Ts...> const& v) const {
             return boost::apply_visitor(*this, v);
        }
        // optionally repeat for non-const `v`
    
        // normal variant handling overloads
    };
    

    这样,您可以简单地将访问者对象用作函数对象。

    【讨论】:

    • 非常有趣。我什至不知道这个电话。但我仍然不确定如何使用它。假设我有auto y=boost::apply_visitor(v1)。如何将y 传递给我的adder 函数?
    • 您可以像在任何地方传递可调用对象一样传递它(使用模板类型参数推断参数类型或使用std::function&lt;&gt; 或类似方法删除该类型)
    • 非常感谢。我认为 Angew 已经向我展示了如何做到这一点。 :-)
    • 确实如此。这也是他方法的一部分
    【解决方案2】:

    首先,您应该将访问者的operator() 重载标记为const

    struct Visitor1 : public boost::static_visitor<int> {
        template<typename T> 
        result_type operator()(const T&) const {
            return 42;
        }
    };
    
    struct Visitor2 : public boost::static_visitor<int> {
        template<typename T>
        result_type operator()(const T&) const {
            return 21;
        }
    };
    

    然后,您需要更改您的加法器以接受模板参数而不是 boost::static_visitor - 它是您的 Visitor1 类型,其中包含带有您想要的逻辑的 operator() 重载。您需要“正确的类型”才能进行访问。

    template <typename T>
    int adder(const T& visitor, const TVar& visitable) {
        return visitable.apply_visitor(visitor) + 1;
    }
    

    live wandbox example

    【讨论】:

    • 哎哟!如此明显。谢谢您的帮助。我真的无法习惯这个模板。
    【解决方案3】:

    static_visitor 不是多态基类,不能这样对待。顾名思义:它是一个static(静态类型)访问者。您需要访问者的 static 类型是您要调用的类型。请注意,这是不可避免的,因为成员函数模板不能是虚拟的。在您的情况下,这意味着 adder 必须成为一个函数模板,由访问者类型模板化:

    template <class Visitor>
    int adder(const Visitor& visitor, const TVar& visitable) {
      return visitable.apply_visitor(visitor) + 1;
    }
    

    main 中的使用将保持不变。

    如果您无法将adder 制作为模板,您可以将@sehe's answer 提到的apply_visitorstd::function 结合使用(如果您不能使用C++11,则可以使用boost::function):

    int adder(std::function<int(const TVar&)> visitorApplier, const TVar& visitable)
    {
      return visitorApplier(visitable);
    }
    
    int main(int argc, char **args) {
        Visitor1 v1;
        Visitor2 v2;
        TVar x;
    
        std::cout << adder(boost::apply_visitor(v1), x) << std::endl;
        std::cout << adder(boost::apply_visitor(v2), x) << std::endl;
    }
    

    [Live example]

    从技术上讲,访问者本身已经是一个合适的可调用对象,因此即使没有明确使用apply_visitor,它也应该可以工作:

    int main(int argc, char **args) {
        Visitor1 v1;
        Visitor2 v2;
        TVar x;
    
        std::cout << adder(v1, x) << std::endl;
        std::cout << adder(v2, x) << std::endl;
    }
    

    【讨论】:

    • 似乎是一个非常酷的解决方案。不幸的是,它没有编译。我的 Visual Studio 编译器有问题吗?它应该支持 C++11。
    • 它说从boost::apply_visitor_delayed_t&lt;Visitor1&gt; 转换为const std::function&lt;int (void)&gt; &amp; 是不可能的。我将传递的变量 visitorApplier 设为 const 引用。
    • 刚刚发现,您可以将adder(boost::apply_visitor(v1), x) 替换为adder(v1, x)。因此,您甚至可以缩短解决方案。
    猜你喜欢
    • 1970-01-01
    • 2011-06-30
    • 1970-01-01
    • 1970-01-01
    • 2021-03-22
    • 1970-01-01
    • 2013-04-25
    • 2014-02-22
    • 1970-01-01
    相关资源
    最近更新 更多