【问题标题】:std::bind assigned to std::functionstd::bind 分配给 std::function
【发布时间】:2016-12-18 00:24:36
【问题描述】:

我想要一个std::function<void(char**)> 类型的变量。这是一个未能做到这一点的简单示例。

我想了解的是:

  • 当我调用jj_2a(5, 6) 时,编译器没有 抱怨的 auto 是什么?该函数已绑定所有参数。
  • 但如果我不使用 auto,我会得到我期望的行为(编译错误带有参数)。很明显,function<void(void)> 根本不是 auto 决定的。
  • 如果我绑定第一个参数而不是第二个参数 (jj_3),则使用两个参数调用(但根据我的心智模型会丢弃错误的参数)同时使用一个参数调用(我认为应该可以)无法编译。
  • std::functional 用于jj_3_f 表示“没有可行的转换”,尽管到目前为止错误消息对我没有帮助。

有关编译器和特定错误,请参见下文。这是linux,clang 3.8.0,ubuntu 16.04.1。

#include <functional>
#include <iostream>

void jj_1(int x, int y) { std::cout << x << ' ' << y << std::endl; }

int main() {
  using namespace std::placeholders; // for _1, _2, _3...

  auto jj_2a = std::bind(jj_1, 3, 2);
  jj_2a(5, 6);  // This works, prints "3 2", no compiler warning, is auto drunk?
  jj_2a();      // This also works, prints "3 2".
  std::function<void(void)> jj_2a_f = std::bind(jj_1, 30, 20);
  //jj_2a_f(50, 60);  // Compile error, good!
  jj_2a_f();  // This works, prints "30 20", good!

  auto jj_2b = std::bind(jj_1, _2, _1);
  jj_2b(5, 6);  // This works, prints "6 5", good.

  auto jj_3 = std::bind(jj_1, 3, _2);
  jj_3(5, 6);  // This works, prints "3 6", so it's the first arg that is dropped!
  //jj_3(7);     // Compile error!

  //std::function<void(int)> jj_3_f = std::bind(jj_1, 3, _2);  // Compile error, no viable conversion!
  //jj_4(11);
}

我用这个编译

clang++  -std=c++14 -Wall -Wextra /tmp/foo.cc -o /tmp/foo

jj_3(7) 相关的编译器警告如下:

/tmp/foo.cc:21:7: error: no matching function for call to object of type 'std::_Bind<void (*(int,
  std::_Placeholder<2>))(int, int)>'
  jj_3(7);     // Compile error!
  ^~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/functional:1129:2: note: 
  candidate template ignored: substitution failure [with _Args = <int>]: no viable conversion from
  'std::_No_tuple_element' to 'int'
    operator()(_Args&&... __args)
    ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/functional:1143:2: note: 
  candidate template ignored: substitution failure [with _Args = <int>]: no viable conversion from
  'std::_No_tuple_element' to 'int'
    operator()(_Args&&... __args) const
    ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/functional:1157:2: note: 
  candidate template ignored: substitution failure [with _Args = <int>]: no viable conversion from
  'std::_No_tuple_element' to 'int'
    operator()(_Args&&... __args) volatile
    ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/functional:1171:2: note: 
  candidate template ignored: substitution failure [with _Args = <int>]: no viable conversion from
  'std::_No_tuple_element' to 'int'
    operator()(_Args&&... __args) const volatile
    ^
1 error generated.

jj_3_f 相关的编译器警告如下:

/tmp/foo.cc:23:32: error: no viable conversion from 'typename _Bind_helper<__is_socketlike<void (&)(int,
  int)>::value, void (&)(int, int), int, const _Placeholder<2> &>::type' (aka '_Bind<void (*(int,
  std::_Placeholder<2>))(int, int)>') to 'std::function<void (int)>'
  std::function<void(int)> jj_3_f = std::bind(jj_1, 3, _2);  // Compile error, no viable conversion!
               ^        ~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/functional:2008:7: note: 
  candidate constructor not viable: no known conversion from 'typename
  _Bind_helper<__is_socketlike<void (&)(int, int)>::value, void (&)(int, int), int, const
  _Placeholder<2> &>::type' (aka '_Bind<void (*(int, std::_Placeholder<2>))(int, int)>') to
  'nullptr_t' for 1st argument
  function(nullptr_t) noexcept
  ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/functional:2019:7: note: 
  candidate constructor not viable: no known conversion from 'typename
  _Bind_helper<__is_socketlike<void (&)(int, int)>::value, void (&)(int, int), int, const
  _Placeholder<2> &>::type' (aka '_Bind<void (*(int, std::_Placeholder<2>))(int, int)>') to 'const
  std::function<void (int)> &' for 1st argument
  function(const function& __x);
  ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/functional:2028:7: note: 
  candidate constructor not viable: no known conversion from 'typename
  _Bind_helper<__is_socketlike<void (&)(int, int)>::value, void (&)(int, int), int, const
  _Placeholder<2> &>::type' (aka '_Bind<void (*(int, std::_Placeholder<2>))(int, int)>') to
  'std::function<void (int)> &&' for 1st argument
  function(function&& __x) : _Function_base()
  ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/functional:2054:2: note: 
  candidate template ignored: substitution failure [with _Functor = std::_Bind<void (*(int,
  std::_Placeholder<2>))(int, int)>, $1 = void]: no type named 'type' in
  'std::result_of<std::_Bind<void (*(int, std::_Placeholder<2>))(int, int)> (int)>'
    function(_Functor);
    ^
1 error generated.

Fwiw,我真正想做的是并行。我有一个功能

void MyFunction(A& a, B& b, const char** thing);

其中AB 是类名。我有另一个函数需要回调:

C DoStuff(const std::string& s, std::function<void(const char** thing)> f);

然后我尝试调用它

DoStuff("Hello!", std::bind(MyFunction, an_a, a_b, _3));

我收到关于没有可行转换的错误。

【问题讨论】:

  • std::bind 函数返回一个未指定类型的对象。您不能依赖该类型来限制合法操作。如果您执行合法操作,您将获得理智的结果。但是编译器不能保护你不做错事。未指定的类型不是类型安全的。
  • 啊哈,所以即使在编译时它也无法确定类型。好的,我会接受实现std::bind 是我不擅长的模板编程级别。但是为什么我对std::function 的转换失败了?以及为什么jj_3(7) 无法编译?
  • @ecatmur 是的,这几乎是一个骗局。
  • 不要使用 std::bind。请改用 lambda 表达式。

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


【解决方案1】:

注意:此答案假设您已阅读Why do objects returned from bind ignore extra arguments?

std::bind 返回的对象不是std::function;它是一个未指定类型的对象,在许多方面与std::function 不同;对于您的情况,最重要的是,它会忽略传递给它的任何额外参数。

如果您在绑定表达式中使用最多 _2(例如)的占位符,则可以使用任意数量的参数调用返回的对象,只要该数量至少为 2。

  • 当我调用 jj_2a(5, 6) 时,编译器没有报错的 auto 是什么?该函数已绑定所有参数。

你传递了额外的参数;这些额外的参数将被忽略。

  • 但如果我不使用自动,我会得到我期望的行为(编译错误与参数)。很明显,功能根本不是自动决定的。

正确; bind 不返回 std::function,它返回一个未指定类型的可调用对象,该对象可以(取决于签名)用于构造 std::function

  • 如果我绑定第一个参数而不是第二个 (jj_3),则使用两个参数调用有效(但根据我的心智模型丢弃了错误的参数),而使用一个参数调用(我认为应该有效)不会t 编译。

如果您使用std::placeholders::_2,则必须至少传递 2 个参数。 std::placeholders::_2 选择传递给从 bind 返回的对象的第二个参数。

  • 对 jj_3_f 使用 std::functional 会说“没有可行的转换”,尽管到目前为止错误消息对我没有帮助。

如果你使用std::placeholders::_2,你必须至少传递2个参数; std::function 的构造函数会检查这个。

【讨论】:

    【解决方案2】:

    这不会编译:

     jj_3(7);     // Compile error!
    

    因为您之前说过 jj_3 将第二个参数绑定到调用位置的第二个参数。因此,如果不更改它,您将不得不调用:

     jj_3(0, 7); // 0 is notused
    

    或者更确切地说,将您的 jj_3 声明更改为:

     auto jj_3 = std::bind(jj_1, 3, _1);
    

    这个:

     std::function<void(int)> jj_3_f = std::bind(jj_1, 3, _2);  // Compile error, no viable conversion!
    

    没有编译,因为你应该有:

     std::function<void(int, int)> jj_3_f
    

    正如我上面所说,如果您使用占位符 _2,那么您希望您的 jj_3_f 使用两个参数来调用。

    【讨论】:

      【解决方案3】:

      在一行

      std::function<void(int)> jj_3_f = std::bind(jj_1, 3, _2);  // Compile error, no viable conversion!
      

      你需要替换为

      std::function<void(int,int)> jj_3_f = std::bind(jj_1, 3, _2);
      

      或者通过

      std::function<void(int,int)> jj_3_f = std::bind(jj_1, 3, _2);
      

      行:

       auto jj_3 = std::bind(jj_1, 3, _2);
      

      你需要替换为

      auto jj_3 = std::bind(jj_1, 3, _1);
      

      你只需要一个参数所以你应该写_1

      【讨论】:

        【解决方案4】:

        替换:

        //std::function<void(int)> jj_3_f = std::bind(jj_1, 3, _2);  // Compile error, no viable conversion!
        //jj_4(11);
        

        与:

        std::function<void(int)> jj_3_f = std::bind(jj_1, 3, _1);
        jj_3_f(5);
        

        占位符是结果函数中参数的编号(一个参数,然后是第一个占位符),而不是“绑定”函数中的参数。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-05-23
          • 2012-10-29
          • 1970-01-01
          • 2013-04-16
          • 2019-02-20
          • 1970-01-01
          相关资源
          最近更新 更多