【问题标题】:Boost variant visitor with an extra parameter使用额外参数提升变体访问者
【发布时间】:2015-04-14 03:25:55
【问题描述】:

我有类似下面的代码。

typedef uint32_t IntType;
typedef IntType IntValue;
typedef boost::variant<IntValue, std::string>  MsgValue;

MsgValue v;

不要这么说,

IntValue value = boost::apply_visitor(d_string_int_visitor(), v);

我想像这样传递一个额外的参数: 但是 operator() 给出了编译错误。

//This gives an error since the overload below doesn't work.
IntValue value = boost::apply_visitor(d_string_int_visitor(), v, anotherStr);

class d_string_int_visitor : public boost::static_visitor<IntType>
{
public:
    inline IntType operator()(IntType i) const
    {
        return i;
    }

    inline IntValue operator()(const std::string& str) const noexcept
    {
        // code in here
    }

    //I want this, but compiler error.
    inline IntValue operator()(const std::string& str, const std::string s) const noexcept
    {
        // code in here
    }
};

【问题讨论】:

  • 你想用anotherStr做什么?
  • 为什么不把anotherStr传递给d_string_int_visitor的构造函数然后使用呢?
  • Preatorian 和 Yakk 的解决方案不是通用的;它们之所以起作用,只是因为额外参数的类型是有界类型之一。这就是所谓的二元(多)访问。如果额外类型不是有界类型,则唯一的解决方案是使用构造函数和成员变量,正如 Nawaz 指出的那样。

标签: c++ c++11 boost c++14 boost-variant


【解决方案1】:

您可以使用std::bind 将额外的string 参数绑定到访问者。首先,将std::string 参数添加到所有访问者的operator() 重载中。

class d_string_int_visitor : public boost::static_visitor<IntType>
{
public:
    inline IntType operator()(IntType i, const std::string& s) const
    {
        return i;
    }

    inline IntValue operator()(const std::string& str, const std::string& s) const noexcept
    {
        // code in here
        return 0;
    }
};

现在创建一个您已绑定第二个 string 参数的访问者。

auto bound_visitor = std::bind(d_string_int_visitor(), std::placeholders::_1, "Hello World!");
boost::apply_visitor(bound_visitor, v);

Live demo

但是,更好的解决方案是将字符串作为访问者的构造函数参数传递。

【讨论】:

  • 感谢 Nawaz,Praetorian,由于某种原因,我有一个盲点,无法将其传递给 d_string_int_visitor。但我真的很高兴,因为这个解决方案实际上扩展了我对 boost bind 的理解。
  • 为什么要在 C++14 中使用 std::bind 呢?
  • @Yakk Habit 我猜(我仍然在工作中编写 C++03)。但是,恕我直言,与bind 解决方案相比,这是 lambda 解决方案似乎不那么可口的少数情况之一。
  • @Praetorian 我的问题是,从我的角度来看,bind 是以前版本的 C++ 中用于小众问题的一个不起眼的古怪函数;同时 lambda 在现代 C++ 中非常重要。我希望 C++11 开发人员每周(如果不是每天)在各种情况下(不仅仅是这个)都使用 lambdas。而bind 真的很古怪,尤其是如果你开始递归(!)。
【解决方案2】:
typedef uint32_t IntType;
typedef IntType IntValue;
typedef boost::variant<IntValue, std::string>  MsgValue;

MsgValue v;

IntValue value = boost::apply_visitor([&](auto&& one){
  return d_string_int_visitor{}(decltype(one)(one), anotherStr);
}, v);

假设d_string_int_visitor 的每个重载都可以处理额外的参数。

作为奖励,您甚至可以根据需要取消包装类:

IntValue to_int_value(IntValue v, std::string const& format) { return v; }
IntValue to_int_value(std::string const& str, std::string const& format);

IntValue value = boost::apply_visitor([&](auto&& one){
  return to_int_value(decltype(one)(one), anotherStr);
}, v);

我们创建了一个匿名 lambda,它转发给一组传统的函数重载。

auto&amp;&amp; onedecltype(one)(one) 是一种从 lambda (C++14) 进行完美转发的技术。您可以用std::forward&lt;decltype(one)&gt;(one) 替换第二个,但我发现短版本可读。与 std::forward 不同,它对值类型做了“错误”的事情,但我们知道 one 是一个 l 或 r 值引用。

【讨论】:

  • 请注意,这至少需要 Boost 1.58。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-05
  • 2016-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多