C ++ 1y没有从std :: bind到std :: function的可行转换

时间:2014-06-10 06:57:43

标签: c++ c++11 c++14

我正在尝试将转发函数存储到std::function中。如果我使用std::bind,则会收到no viable conversion from ...之类的错误消息。如果我使用lambda,它编译好了。

以下是示例代码

#include <functional>

template<typename Handler>void func1(int a, Handler&& handler) {}
template<typename Handler>void func2(Handler&& handler)
{
    // this line compile fine
    std::function<void ()> funcA = [handler = std::move(handler)]() { func1(1, std::move(handler)); };
    // this line got compile error
    std::function<void ()> funcB = std::bind(func1<Handler>, 1, std::move(handler));
}

int main()
{
    func2(&main); // this just a sample, I am using functor as argument in real code
}

同时尝试 g ++ --std = c ++ 1y (v4.9.0)和 clang ++ --std = c ++ 1y (v3.4.1)相同的结果

编辑:clang ++错误消息

main.cpp:8:28: error: no viable conversion from 'typename _Bind_helper<__is_socketlike<void (*)(int, int (*&&)())>::value, void (*)(int, int
      (*&&)()), int, int (*)()>::type' (aka '_Bind<__func_type (typename decay<int>::type, typename decay<int (*)()>::type)>') to
      'std::function<void ()>'
    std::function<void ()> funcB = std::bind(&func1<Handler>, 1, std::move(handler));
                           ^       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:14:5: note: in instantiation of function template specialization 'func2<int (*)()>' requested here
    func2(&main);
    ^
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.0/../../../../include/c++/4.9.0/functional:2181:7: note: candidate constructor not viable: no
      known conversion from 'typename _Bind_helper<__is_socketlike<void (*)(int, int (*&&)())>::value, void (*)(int, int (*&&)()), int, int
      (*)()>::type' (aka '_Bind<__func_type (typename decay<int>::type, typename decay<int (*)()>::type)>') to 'nullptr_t' for 1st argument
      function(nullptr_t) noexcept
      ^
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.0/../../../../include/c++/4.9.0/functional:2192:7: note: candidate constructor not viable: no
      known conversion from 'typename _Bind_helper<__is_socketlike<void (*)(int, int (*&&)())>::value, void (*)(int, int (*&&)()), int, int
      (*)()>::type' (aka '_Bind<__func_type (typename decay<int>::type, typename decay<int (*)()>::type)>') to 'const std::function<void ()> &'
      for 1st argument
      function(const function& __x);
      ^
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.0/../../../../include/c++/4.9.0/functional:2201:7: note: candidate constructor not viable: no
      known conversion from 'typename _Bind_helper<__is_socketlike<void (*)(int, int (*&&)())>::value, void (*)(int, int (*&&)()), int, int
      (*)()>::type' (aka '_Bind<__func_type (typename decay<int>::type, typename decay<int (*)()>::type)>') to 'std::function<void ()> &&' for
      1st argument
      function(function&& __x) : _Function_base()
      ^
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.0/../../../../include/c++/4.9.0/functional:2226:2: note: candidate template ignored:
      substitution failure [with _Functor = std::_Bind<void (*(int, int (*)()))(int, int (*&&)())>]: no matching function for call to object of
      type 'std::_Bind<void (*(int, int (*)()))(int, int (*&&)())>'
        function(_Functor);
        ^
1 error generated.

2 个答案:

答案 0 :(得分:17)

引言

std :: bind 将尝试使用 lvalue-reference 调用func1<Handler>,但 func1 的实例化将使它只接受 rvalues


说明

在这里,我们将您的测试用例减少到最低限度,以显示正在发生的事情,下面的代码片段格式错误,并解释为什么会这样。

#include <functional>

template<class T>
void foobar (T&& val);

int main() {
  std::function<void()> f = std::bind (&foobar<int>, std::move (123));
}

在上面我们将使用foobar实例化T = int,这使得参数类型 val 成为 rvalue-reference 到< em> int (int&&)。

std::move(123) move-construct 我们的值存储在由 std :: bind 创建的对象中,但标准说当 std :: bind 稍后调用存储的函数,所有参数都传递为TiD cv &;即。 as lvalues

此行为由标准(n3797)强制执行,如[func.bind.bind]p10部分所述。


通过将以前格式错误的代码段更改为以下内容,不会引发任何错误,因为foobar<int>现在接受左值引用;适合绑定到由 std :: bind 返回的 function-object 传递给我们函数的 lvalue

  std::function<void()> f = std::bind (&foobar<int&>, std::move (123));

???

#include <functional>
#include <type_traits>
#include <iostream>

int main() {
  auto is_lvalue = [](auto&& x) {
    return std::is_lvalue_reference<decltype(x)> { };
  };

  auto check = std::bind (is_lvalue, std::move (123));
  bool res   = check (); // res = true
}

答案 1 :(得分:1)

简而言之:function必须是可复制的。带有rvalue的bind返回不可复制的对象。解决方法是使用包含上述值的shared_ptr捕获/绑定