【问题标题】:How to boost::bind a template member function which takes a universal reference as a parameter如何 boost::bind 以通用引用作为参数的模板成员函数
【发布时间】:2017-06-10 22:04:58
【问题描述】:

我一直在尝试使用 boost::bind 将对成员函数的调用发布到 io_strand 上,但一直出错。我已经设法为我正在尝试做的事情创建一个简单的等效示例,并且在以下上下文中看到了相同的错误:

我有以下包含我要调用的 doThings() 成员函数的类:

class base
{
public:
  int x = 1;

  template<typename B>
  void doThings(B&& b)
  {}
};

然后有一个子类(以准确表示我遇到错误的场景 - 我认为这没有区别)

class child : public base
{
  int y = 2;
};

我有以下代码试图进行 boost::bind 调用:

template<typename A>
void getInt(child c, A&& a)
{
  boost::bind((void(child::*)(A)) &child::doThings, &c, std::forward<A>(a))();
}

然后调用如下:

int main()
{
  child c = child();
  getInt(c, 7);
}

当我编译上面的代码时,我得到以下错误:

错误:没有匹配转换函数'doThings'到类型'void(类 孩子::*)(int)'


如果我将 doThings() 的函数签名更改为采用常规 B 类型而不是通用引用,即 B 而不是 B&amp;&amp; 然后它编译运行没有问题。
我怀疑我的问题与我在 getInt() 中所做的演员表有关:

(void(child::*)(A))

但我不知道我需要将其更改为什么。 A&amp;&amp; 在这种情况下不起作用,因为我相信它会代表这种情况下的 r 值引用。我尝试时遇到的编译错误似乎证实了这一点:

错误:无法将‘int’左值绑定到‘int&&’

为了完整性:如果我不尝试执行强制转换,则会收到以下编译错误:

错误:没有匹配的函数调用'bind(未解决的重载 函数类型, child*, int)'

有人可以告诉我我需要做什么才能使我的 boost::bind 调用在这种情况下有效吗?

我正在使用 C++11

【问题讨论】:

  • 您知道您将int 传递给doThings 对吗?
  • 你可以使用带有 simpler 语法的 lambda:[&amp;](){ c.doThings(std::forward&lt;A&gt;(a));};.
  • @Jonathan Mee 是的,这是个问题吗?我现在意识到 getInt 在这里是一个奇怪的名称选择 - 我一直在弄乱最后一个左右的各种排列,而函数与获取 int 无关的事实已经从我的想法中溜走了。该名称与此处的意图无关
  • 有什么理由不做c.getThings(std::forward&lt;A&gt;(a));

标签: c++ c++11 templates boost universal-reference


【解决方案1】:

我建议不要使用boost::bind,因为lambda expressions 可用于干净地绑定参数(避免bind 中解释的许多陷阱this talk by STL


我假设你想要:

  • 如果将 rvalue-reference 传递给 getInt,则通过移动捕获 a

  • 如果将 lvalue-reference 传递给 getInt,则通过引用捕获 a

我还假设:

  • A 在您的真实代码中不是 int,否则 完美转发 将没有意义。

  • 您希望避免不必要的 a 副本,或者 A 可能是 move-only 类型。

  • 您只能访问 C++11(不能访问更新的标准)

如果您需要“完美捕获” a (即如果A 是右值引用,则按移动捕获,如果@987654340 则按引用捕获@ 是一个左值引用),你需要某种包装器。

不幸的是这是不平凡的,尽管它在 C++14 和 C++17 中变得更好。下面是最终语法 外观的示例:

template<typename A>
void getInt(child c, A&& a)
{
    // `a_wrapper` stores an `A&` or an `A` depending on the value category
    // of `a`. Copying it should not copy `a` - it should conditionally move 
    // it depending on the original value category of `a`.
    auto a_wrapper = forward_capture_wrapper(std::forward<A>(a));

    // `forward_like` retains information about `a`'s value category so that
    // it can be used in the body of the lambda to forward the reference/value
    // stored inside `a_wrapper`.
    //                          vvvvvvvvvvvvvvv
    [&a, a_wrapper]{ c.doThings(forward_like<A>(a_wrapper.get()); }();
    //                                          ^^^^^^^^^^^^^^^
    // `a_wrapper.get()` returns a reference that can then be moved or passed
    // to `c.doThings`.
}

如您所见,您需要一个名为forward_capture_wrapper模板函数 来处理“完美捕获”。您可以在以下资源中找到有关如何实施的信息:

通过结合以上资源,您应该能够在 C++11 中实现“完美的捕获包装器”。

您还需要一个 forward_like 帮助函数来保留 a 参数的原始值类别。你可以找到一个实现:

【讨论】:

  • 您的两个假设都是正确的。抱歉,回想起来,我意识到我在这里对 int 的使用具有误导性
猜你喜欢
  • 2020-02-15
  • 2021-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-01
  • 2013-11-12
  • 2016-06-25
  • 2011-07-06
相关资源
最近更新 更多