std::bind 对我来说毫无意义

std::bind makes no sense to me whatsoever

以下:

#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 中得到的错误是

"class does not define an 'operator()' or a user defined conversion operator to a pointer-to-function or reference-to-function that takes appropriate number of arguments"

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

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

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

其他函数需要参数,因此您必须在 "bind time" 处绑定参数,例如

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:错误:静态断言失败:成员指针的参数数量错误

你写的和这个是等价的,用lambda代替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 的成员函数,但不一定使用正确的参数。

看来你对bind应该做什么有误解。
您传递给 bind 结果的参数是 "substituted" 到占位符 _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).