以下内容:
#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)没有编译。我在Visual Studio 2013中遇到的这3个案例的错误是
&#34;类未定义&#39;运算符()&#39;或者用户定义的转换运算符到指向函数的指针或函数引用,它接受适当数量的参数&#34;
我对模板和标准库的理解有限,但这对我来说似乎没有任何合理意义。是否有合理的简单解释?
答案 0 :(得分:14)
要将参数传递给目标,您需要在绑定表达式中提供这些参数,或者通过向绑定表达式添加占位符使其保持未绑定状态,然后必须使用参数调用该函数来替换占位符。
您可以在没有占位符的情况下致电bar1
,因为它没有任何参数,因此您不需要传递任何内容。传递给f1
的参数只是被忽略,因为没有未绑定的参数,即没有需要替换的占位符。
其他函数需要参数,因此您必须在&#34;绑定时间&#34; e.g。
auto f2a = std::bind(&Foo::bar2, &foo, 1);
f2a();
或保持参数未绑定,并在调用可调用对象时提供它们:
auto f2b = std::bind(&Foo::bar2, &foo, std::placeholders::_1);
f2b(1);
请注意,GCC 5现在具有静态断言来捕获此类错误。如果可以确定目标函数的arity并且绑定表达式没有为目标函数的每个参数设置绑定参数或占位符,那么它会说:
/ 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
的成员函数,但不一定使用正确的参数。
答案 1 :(得分:4)
您似乎对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)
。