我有以下一段代码:
#include <functional>
template <typename T>
class TD; // For displaying type
void f(int, int, int) { }
int main() {
auto g = std::bind(f, std::placeholders::_1, 2, 2);
TD<decltype(g)> td1;
return 0;
}
在此代码中,TD
是一个模板技巧,用于显示通过 decltype
传递的模板参数。
编译器的输出如下(以C++14
模式编译(:
prog.cpp: In function 'int main()':
prog.cpp:10:18: error: aggregate 'TD<std::_Bind<void (*(std::_Placeholder<1>, int, int))(int, int, int)> > td1' has incomplete type and cannot be defined
TD<decltype(g)> td1;
^
好吧,不完整的类型错误当然除外。但是在这个错误消息中让我感到好奇的是std::_Bind<void (*(std::_Placeholder<1>, int, int))(int, int, int)>
.我可以理解std::_Bind
是一个代理类,它定义了operator()
并使我们的目的成为可能。但它的模板参数void (*(std::_Placeholder<1>, int, int))(int, int, int)
让我惊叹!我应该如何解释它?它在用户土地代码中是否有任何用处?如何使用此声明创建自己的类?
void (*(std::_Placeholder<1>, int, int))(int, int, int)
这声明了一个未命名的函数,它接受三个参数(std::_Placeholder<1>
、int
和int
(,并返回一个指针,指向一个函数,该函数需要三个int
并返回void
。
让我们简化一点。首先考虑一个简单的函数声明:
void f(int)
现在,在参数声明(函数或模板(中,您可以省略名称,然后得到
void (int)
如果在函数声明的参数列表中使用,则等效于函数指针void(*)(int)
。
返回函数指针的函数声明如下:
void (*f(int))(int);
// ^ ^ <- this pair of parentheses changes
// the order in which the declaration is parsed.
// Without it, the return type would be `void*`
// and you'd get a syntax error
现在你可以删除名称f
,你基本上得到你问的相同的东西。
它的用途?显然,这在实现std::bind
:)时很有用我现在想不出别的了...
C++11标志着function
的出现,这使得定义函数指针变得更加容易:
std::function 的实例可以存储、复制和调用任何可调用目标 - 函数、lambda 表达式、绑定表达式或其他函数对象,以及指向成员函数的指针和指向数据成员的指针。
例如,假设您需要在其中一个函数中获取一个函数指针以string foo(int param) { return to_string(param); }
。在 C++11 之前,您的函数需要如下所示:
void bar(string (*func)(int)) { cout << func(13) << endl; }
让我们更进一步,假设您想将foo
扩展到:string foo2(int lhs, int rhs) { return to_string(lhs + rhs); }
。但是现在你想把它塞回bar
.bar(bind(&foo2, placeholders::_1, 42));
执行此操作会给您一个如下错误:
无法将参数 '1' 的 'std::_Bind_helper (*((int, int(, const std:::_Placeholder<1>&, int>::type {aka std::_Bind (*(std::_Placeholder<1>, int(((int, int(>}' 转换为 'std::string (*((int( {aka std::basic_string (*((int(}' 对于参数 '1' 到 'void bar(std::string (*((int(('
您可以通过创建一个采用实现特定参数的函数来绕过此错误,例如:void bar2(_Bind<string (*(_Placeholder<1>, int))(int, int)> func) { cout << func(13) << endl; }
可以使用以下方法成功调用:bar2(bind(&foo2, placeholders::_1, 42));
。这是特定于实现的原因是类型:_Bind
和 _Placeholder
是非标准的。事实上,bind
的回报是:
未指定类型 T 的函数对象
这把我们带到了function
.如果尚未关闭函数指针的语法限制,则需要采用function
参数来接受由 bind
创建的对象。让我们使用 function
创建一个新bar
:
void bar3(function<string(int)> func) { cout << func(13) << endl; }
这能够同时接受传统的函数指针和bind
函子。此外,它可以处理 lambda,因此您可以执行以下操作: bar3([](int param) { return to_string(param); });
我创建了一个实时示例,因此您可以尝试使用它,希望function
对象的好处是显而易见的。