多参数的boost::static_visitor

13
typedef boost::variant<int, double> Type;
class Append: public boost::static_visitor<>
{
public:
    void operator()(int)
    {}

    void operator()(double)
    {}

};

Type type(1.2);
Visitor visitor;
boost::apply_visitor(visitor, type);

是否可以更改访问者,以便它接收以下额外数据:

class Append: public boost::static_visitor<>
{
public:
    void operator()(int, const std::string&)
    {}

    void operator()(double, const std::string&)
    {}
};

在 Append 对象的生命周期内,该字符串值会发生更改。在这种情况下,通过构造函数传递字符串不是一个选项。


为什么这不是一个选项呢?如果情况发生变化,您可以创建另一个带有不同字符串的Append对象。 - kennytm
字符串值将在什么时候更改?我不明白为什么不能使用构造函数来完成。 - Mene
@KennyTM Append类通过其构造函数接受多个依赖项。如果我在我的类中重建Append对象,那么这个类也将需要通过其构造函数获取Append使用的所有依赖项。我希望我的类只需通过其构造函数获取一个Append对象。 - Baz
@Baz:你可以在构造函数中使用Append对象,然后在每次调用之前设置特定的值,甚至不需要了解任何其他依赖关系。 - Mankarse
@Mankarse 是的,这正是我想要做的,但似乎有点像黑客攻击,因为在应用访问者之前可能会忘记设置值。 - Baz
1
@Baz:你可以创建一个辅助函数,首先设置Visitor的相关部分,然后调用apply_visitor。我想不到一种在类型系统中强制执行它的方法,但是你可以保留一个跟踪辅助函数是否被调用的bool,如果没有调用,则会断言。 - Mankarse
3个回答

20

每次调用时传递的“额外参数”是 this 指针。使用它来传递你需要的任何其他信息:

#include <boost/variant.hpp>
typedef boost::variant<int, double> Type;
class Append: public boost::static_visitor<>
{
public:
    void operator()(int)
    {}

    void operator()(double)
    {}
    std::string argument;
};

int main() {
    Type type(1.2);
    Append visitor;
    visitor.argument = "first value";
    boost::apply_visitor(visitor, type);
    visitor.argument = "new value";
    boost::apply_visitor(visitor, type);
}

2
如果必要的话,成员不一定需要是 std::string,也可以是例如 std::string* 这样可以重新绑定的类型。 - Luc Danton
@LucDanton:确实——根据具体情况的确切需求,这个一般想法有许多可能的变化。 - Mankarse

5
另一种选择是绑定额外的参数。您的访问者类可能如下所示:
class Append: public boost::static_visitor<>
{
public:
    void operator()(const std::string&, int)
    {}

    void operator()(const std::string&, double)
    {}
};

这样调用:

std::string myString = "foo";
double value = 1.2;
auto visitor = std::bind( Append(), myString, std::placeholders::_1 );
boost::apply_visitor( visitor, value );

1
这个可以解决你的问题:
#include <iostream>
#include <string>
#include <boost/variant.hpp>

typedef boost::variant<int, double> Type;
typedef boost::variant<const std::string> Extra;
class Append: public boost::static_visitor<>
{
public:
    void operator()(const int& a1, const std::string& a2) const {
        std::cout << "arg 1 = "<< a1 << "\n";
        std::cout << "arg 2 = "<< a2 << "\n";
    }

    void operator()(const double& a1, const std::string& a2) const {
        std::cout << "arg 1 = "<< a1 << "\n";
        std::cout << "arg 2 = "<< a2 << "\n";
    }
};

int main()
{
    Type type(1.2);
    Extra str("extra argument");
    boost::apply_visitor(Append(), type, str);
}

这里有一个可用的演示。您可以发送额外的参数 - 任意数量。限制是它们必须包装在boost::variant中。但是,编译器会优化掉单一类型的variant。如果您想要超过两个参数,您必须#include <boost/variant/multivisitors.hpp>,请参见https://www.boost.org/doc/libs/1_70_0/doc/html/boost/apply_visitor.html

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接