lambda 的 return 值的规范不起作用
Specification of lambda's return value doesn't work
我有问题。
函数“__sub
”解析像"1x + (5y - 2)"
这样的字符串。每次它看到“(
”,它都会调用自己来准确解析括号中的内容。
这是一些伪代码,说明了问题:
auto __sub = [&needed_fn](const char *& iter, char end_at) -> int {
for (; *iter != end_at; iter++) {
if () {
int number = needed_fn(iter);
} else if (*iter == '(') {
int sub_result = __sub(iter, ')');
}
}
return 0; // temporarily, as for debugging purposes only needed
};
但这不起作用。起初没有(-> int
)的规范。
在有或没有 return 值的规范的情况下,它都不起作用。
它说:
a.cpp: In lambda function:
a.cpp:97:22: error: use of ‘__sub’ before deduction of ‘auto’
int sub_result = __sub(it, ')');
建议:将 __sub
定义为 std::function<int(const char *, char)>
std::function<int(const char * &, char)> __sub;
__sub = [&needed_fn](const char *& iter, char end_at) -> int {
for (; *iter != end_at; iter++) {
if ( /* ??? */ ) {
int number = needed_fn(iter);
} else if (*iter == '(') {
int sub_result = __sub(iter, ')');
}
return 0;
};
否则编译器无法在 __sub()
.
的主体内使用相同的 __sub()
推断 (auto
) __sub()
的类型
我不同意这是一个先有鸡还是先有蛋的问题,或者至少是一个可以解决的问题的说法,相反我建议这是语言的一个怪癖,因为你几乎可以完成手工做同样的事情。
为了稍微简化讨论,举一个常见的递归例子,阶乘(godbolt):
auto factorial = [](int n) {
if (n == 0)
return 1;
else
return n * factorial(n - 1);
};
失败并出现您看到的错误:
<source>: In lambda function:
<source>:7:24: error: use of 'factorial' before deduction of 'auto'
7 | return n * factorial(n-1);
| ^~~~~~~~~
但是factorial
是一个自动存储时长的变量,所以不捕获是不能引用的,不捕获代码肯定是不正确。按值捕获没有意义,因为 lambda 类型将包含其自身的副本。这将与典型的 C++ classes 不一致,后者不能包含自身的副本,即使否则为空。因此,必须通过引用捕获 (godbolt):
auto factorial = [&factorial](int n) {
if (n == 0)
return 1;
else
return n * factorial(n - 1);
};
我们的代码现在更正确了。编译器怎么说?
<source>:3:24: error: use of 'factorial' before deduction of 'auto'
3 | auto factorial = [&factorial](int n) {
| ^~~~~~~~~
<source>: In lambda function:
<source>:7:24: error: use of 'factorial' before deduction of 'auto'
7 | return n * factorial(n - 1);
| ^~~~~~~~~
更多错误! lambda 只是函数对象的语法糖,所以让我们退后一步,看看不加糖的形式是否可以正常工作 (godbolt):
struct factorial_t
{
factorial_t& factorial;
auto operator()(int n) const
{
if (n == 0)
return 1;
else
return n * factorial(n - 1);
}
};
int main()
{
factorial_t factorial{factorial};
}
这行得通,在一个完美的世界中,也许 lambda 形式也行。 factorial
中的auto
在推导之前,很像一个不完整的类型。 C++ 允许对不完整类型的引用和指针,包括对 class 或包含它们的结构的引用和指针。而 lambda 引用捕获只是引用或指针。因此,在语言的精神范围内,这一切都是可能的。另一种语言可以将 factorial
的类型推断为 lambda 的类型,而 lambda 类型是不完整的,即在尝试为 lambda 类型创建定义之前。
在 C++ 中,您有几个可能的解决方案。首先,您可以手写闭包类型(如the third example)。
其次,您可以删除类型,如另一个答案(godbolt):
std::function<int(int)> factorial = [&factorial](int n) {
if (n == 0)
return 1;
else
return n * factorial(n - 1);
};
请注意,另一个答案缺少关键的捕获。
三、可以延时需要的类型(godbolt):
auto factorial = [](int n, auto&& factorial) {
if (n == 0)
return 1;
else
return n * factorial(n - 1, factorial);
};
通过使调用运算符成为模板来延迟对类型的需求,但代价是使用起来很尴尬,例如factorial(4, factorial)
。即使是小级别的间接访问也是可以克服的(godbolt):
auto factorial_impl = [](int n, auto&& factorial_impl) {
if (n == 0)
return 1;
else
return n * factorial_impl(n - 1, factorial_impl);
};
auto factorial = [&factorial_impl](int n) {
return factorial_impl(n, factorial_impl);
};
希望对您有所帮助!
我有问题。
函数“__sub
”解析像"1x + (5y - 2)"
这样的字符串。每次它看到“(
”,它都会调用自己来准确解析括号中的内容。
这是一些伪代码,说明了问题:
auto __sub = [&needed_fn](const char *& iter, char end_at) -> int {
for (; *iter != end_at; iter++) {
if () {
int number = needed_fn(iter);
} else if (*iter == '(') {
int sub_result = __sub(iter, ')');
}
}
return 0; // temporarily, as for debugging purposes only needed
};
但这不起作用。起初没有(-> int
)的规范。
在有或没有 return 值的规范的情况下,它都不起作用。
它说:
a.cpp: In lambda function:
a.cpp:97:22: error: use of ‘__sub’ before deduction of ‘auto’
int sub_result = __sub(it, ')');
建议:将 __sub
定义为 std::function<int(const char *, char)>
std::function<int(const char * &, char)> __sub;
__sub = [&needed_fn](const char *& iter, char end_at) -> int {
for (; *iter != end_at; iter++) {
if ( /* ??? */ ) {
int number = needed_fn(iter);
} else if (*iter == '(') {
int sub_result = __sub(iter, ')');
}
return 0;
};
否则编译器无法在 __sub()
.
__sub()
推断 (auto
) __sub()
的类型
我不同意这是一个先有鸡还是先有蛋的问题,或者至少是一个可以解决的问题的说法,相反我建议这是语言的一个怪癖,因为你几乎可以完成手工做同样的事情。
为了稍微简化讨论,举一个常见的递归例子,阶乘(godbolt):
auto factorial = [](int n) {
if (n == 0)
return 1;
else
return n * factorial(n - 1);
};
失败并出现您看到的错误:
<source>: In lambda function:
<source>:7:24: error: use of 'factorial' before deduction of 'auto'
7 | return n * factorial(n-1);
| ^~~~~~~~~
但是factorial
是一个自动存储时长的变量,所以不捕获是不能引用的,不捕获代码肯定是不正确。按值捕获没有意义,因为 lambda 类型将包含其自身的副本。这将与典型的 C++ classes 不一致,后者不能包含自身的副本,即使否则为空。因此,必须通过引用捕获 (godbolt):
auto factorial = [&factorial](int n) {
if (n == 0)
return 1;
else
return n * factorial(n - 1);
};
我们的代码现在更正确了。编译器怎么说?
<source>:3:24: error: use of 'factorial' before deduction of 'auto'
3 | auto factorial = [&factorial](int n) {
| ^~~~~~~~~
<source>: In lambda function:
<source>:7:24: error: use of 'factorial' before deduction of 'auto'
7 | return n * factorial(n - 1);
| ^~~~~~~~~
更多错误! lambda 只是函数对象的语法糖,所以让我们退后一步,看看不加糖的形式是否可以正常工作 (godbolt):
struct factorial_t
{
factorial_t& factorial;
auto operator()(int n) const
{
if (n == 0)
return 1;
else
return n * factorial(n - 1);
}
};
int main()
{
factorial_t factorial{factorial};
}
这行得通,在一个完美的世界中,也许 lambda 形式也行。 factorial
中的auto
在推导之前,很像一个不完整的类型。 C++ 允许对不完整类型的引用和指针,包括对 class 或包含它们的结构的引用和指针。而 lambda 引用捕获只是引用或指针。因此,在语言的精神范围内,这一切都是可能的。另一种语言可以将 factorial
的类型推断为 lambda 的类型,而 lambda 类型是不完整的,即在尝试为 lambda 类型创建定义之前。
在 C++ 中,您有几个可能的解决方案。首先,您可以手写闭包类型(如the third example)。
其次,您可以删除类型,如另一个答案(godbolt):
std::function<int(int)> factorial = [&factorial](int n) {
if (n == 0)
return 1;
else
return n * factorial(n - 1);
};
请注意,另一个答案缺少关键的捕获。
三、可以延时需要的类型(godbolt):
auto factorial = [](int n, auto&& factorial) {
if (n == 0)
return 1;
else
return n * factorial(n - 1, factorial);
};
通过使调用运算符成为模板来延迟对类型的需求,但代价是使用起来很尴尬,例如factorial(4, factorial)
。即使是小级别的间接访问也是可以克服的(godbolt):
auto factorial_impl = [](int n, auto&& factorial_impl) {
if (n == 0)
return 1;
else
return n * factorial_impl(n - 1, factorial_impl);
};
auto factorial = [&factorial_impl](int n) {
return factorial_impl(n, factorial_impl);
};
希望对您有所帮助!