【问题标题】:std::bind makes no sense to me whatsoeverstd::bind 对我来说毫无意义
【发布时间】:2015-06-19 00:45:24
【问题描述】:

以下内容:

#include <functional>

struct Foo 
{
    void bar1() {}
    void bar2(int) {}
    void bar3(int, int) {}
    void bar4(int, int, int) {}
};


int main()
{
    Foo foo;

    auto f1 = std::bind(&Foo::bar1, &foo);
    auto f2 = std::bind(&Foo::bar2, &foo);
    auto f3 = std::bind(&Foo::bar3, &foo);
    auto f4 = std::bind(&Foo::bar4, &foo);

    f1(1, 2, 3, 4); // success
    f2(1, 2, 3);    // error
    f3(1, 2);       // error
    f4(1);          // error    

    return 0;
}

f1(1, 2, 3, 4) 编译并执行 bar1()。 f2(1, 2, 3), 不编译, f3(1, 2) 不编译(但 bar3 有正确的原型), f4(1) 不编译。对于这 3 种情况,我使用 Visual Studio 2013 得到的错误是

“类没有定义一个 'operator()' 或用户定义的转换运算符到一个指向函数的指针或函数的引用,它接受适当数量的参数”

我对模板和标准库的了解有限,但这对我来说似乎没有任何逻辑意义。有合理的简单解释吗?

【问题讨论】:

  • 您可能忘记在f2f3f4 中放置占位符(_1、_2、_3)。 @Matteo Italia bind 有时比 lambda 更简洁。
  • 好的,所以绑定到 bar1() 的 f1(1, 2, 3, 4) 不需要任何占位符。但是因为 operator() 不“检查” arity,所以 bar1() 被调用并且 (1, 2, 3, 4) 被模板魔法丢弃,没有错误。
  • void main 同样是错误的。
  • @sbabbi:我不认为(很少)更简洁的表示法可以证明更糟糕的语法和可怕的错误消息,只要你搞砸了。 boost::bind 只是使 STL 的功能部分模糊地在 C++03 中可用的一种解决方法,我不明白为什么我们应该继续使用这样的组合,因为我们终于获得了核心语言级别的真实事物(lambdas,最重要的是闭包)。与 std::auto_ptr 和其他针对语言缺陷的解决方法一样,是时候忘记它曾经存在过。
  • 如果没有类型擦除,您也无法将所有绑定结果存储在一个数组中。如果你使用std::function,那么它与 lambdas 配合得很好。

标签: c++ std-function


【解决方案1】:

要将参数传递给目标,您需要在绑定表达式中提供这些参数,或者通过向绑定表达式添加占位符使它们不绑定,然后您必须使用要替换占位符的参数调用函数。

你可以不带占位符调用bar1,因为它不接受任何参数,所以你不需要传递任何东西给它。您传递给 f1 的参数将被忽略,因为没有未绑定的参数,即没有需要替换的占位符。

其他函数需要参数,因此您必须在“绑定时间”绑定参数,例如

auto f2a = std::bind(&Foo::bar2, &foo, 1);
f2a();

或不绑定参数并在调用可调用对象时提供它们:

auto f2b = std::bind(&Foo::bar2, &foo, std::placeholders::_1);
f2b(1);

请注意,GCC 5 现在有静态断言来捕捉这种错误。如果可以确定目标函数的数量并且绑定表达式没有目标函数的每个参数的绑定参数或占位符,那么它会说:

/usr/local/gcc-head/include/c++/5.0.0/functional:1426:7:错误:静态断言失败:指向成员的参数数量错误

你写的和这个是等价的,用 lambdas 代替 bind:

Foo foo;

auto f1 = [&foo](...) { return foo.bar1(); };
auto f2 = [&foo](...) { return foo.bar2(); };
auto f3 = [&foo](...) { return foo.bar3(); };
auto f4 = [&foo](...) { return foo.bar4(); };

f1(1, 2, 3, 4); // success
f2(1, 2, 3);    // error
f3(1, 2);       // error
f4(1);          // error   

即您定义的函子将接受任何参数,但忽略它们,然后调用Foo 的成员函数,但不一定使用正确的参数。

【讨论】:

    【解决方案2】:

    您似乎对bind 应该做什么有误解。
    传递给bind 结果的参数被“替换”到占位符_1_2 等等。与占位符不对应的参数是未使用的,因此被忽略。由于您没有在绑定表达式中使用任何占位符,因此对bar1 的内部调用始终等同于foo.bar1() - 因此它独立于您传递给bind 结果的参数。

    现在很明显,对bar4 的调用等效于foo.bar4(),但bar4 需要四个参数,所以这是无稽之谈。你可以通过写来解决这个问题

    using namespace std::placeholders;
    auto f4 = std::bind(&Foo::bar4, &foo, _1, _2, _3, _4);
    

    现在,如果您提供四个参数,这些参数将被正确传递,并且内部调用等效于例如foo.bar4(1, 2, 3, 4).

    【讨论】:

      猜你喜欢
      • 2022-01-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-02
      • 2014-04-21
      • 2015-02-14
      • 1970-01-01
      相关资源
      最近更新 更多