以下是完整示例:
auto callSelf = [](auto& func) {func(func);};
class wrapper : public decltype(callSelf) {
using base = decltype(callSelf);
public:
wrapper() : base(callSelf) {}
template<class T>
void operator()(T& func) {
base::operator()(func);
}
};
int main()
{
//callSelf(callSelf); // Error
wrapper w;
w(w); // OK, nice endless recursion
}
为什么包装器可以直接执行,导致以下错误?
main.cpp:30: error: use of '<lambda(auto:1&)> [with auto:1 = <lambda(auto:1&)>]' before deduction of 'auto'
auto callSelf = [&](auto& func) {func(func);};
~~~~^~~~~~
答案 0 :(得分:16)
这实际上非常棘手。您正在执行的规则是[dcl.spec.auto]:
如果需要具有未减少占位符类型的实体类型来确定表达式的类型,则程序格式不正确。
这就是错误:
auto callSelf = [](auto& func) {func(func);};
callSelf(callSelf);
我们需要知道callSelf
的类型来确定func(func)
的表达式的类型,它是自动循环的。只需指定返回类型即可轻松解决:
auto callSelf = [](auto& func) -> void {func(func);};
callSelf(callSelf); // ok. I mean, infinite recursion, but otherwise ok. ish.
但是,当你 wrap lambda时,你会得到不同的行为。这一行:
w(w);
将wrapper
类型的对象有效地传递给lambda。那不是它自己的类型。 lambda的主体调用该对象本身,但我们知道该表达式的类型。你声明了它:
template<class T>
void operator()(T& func) {
~~~~~
这个函数对void
有效(对于某些作品的定义),原因与我们添加-> void
时lambda的工作原理相同。它不再是一个不受限制的占位符。我们已经知道了返回类型。要获得与lambda相同的行为,请将operator()
的声明更改为auto
。
答案 1 :(得分:8)
在您的情况下,只需定义返回类型,编译器就应该接受它:
auto callSelf = [](auto& func) -> void {func(func);};
class wrapper : public decltype(callSelf) {
using base = decltype(callSelf);
public:
wrapper() : base(callSelf) {}
template<class T>
void operator()(T& func) {
base::operator()(func);
}
};
int main()
{
callSelf(callSelf); //works
wrapper w;
w(w); //ok, nice endless recursion
}
使用返回类型推导,编译器不能在lambda本身中使用lambda,因为编译器必须查看函数体以推导返回类型。编译器必须检查函数体的事实使它看到使用lambda本身的lambda的内容。由于编译器处于演绎过程中,因此无法使用lambda,因此编译错误。