我有问题。
函数“ __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
)的规范。
无论是否指定返回值,这在两种情况下都不起作用。
它说:
a.cpp: In lambda function:
a.cpp:97:22: error: use of ‘__sub’ before deduction of ‘auto’
int sub_result = __sub(it, ')');
答案 0 :(得分:2)
建议:将__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;
};
否则,编译器无法在auto
主体中使用相同的__sub()
来推断__sub()
的类型。{p}
答案 1 :(得分:1)
我将不同意这是一个鸡鸡蛋问题,或者至少是一个可解决的问题,并建议这是该语言的怪癖,因为您几乎可以完成同一件事用手。
为稍微简化讨论,请使用一个通用的递归示例,阶乘(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 ++类不一致,即使它们为空,它们也不能包含其自身的副本。因此,必须通过引用(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形式也可以。在推导auto
中的factorial
之前,它很像 一个不完整的类型。在C ++中允许对不完整类型的引用和指针,包括对包含它们的类或结构的引用和指针。而且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);
};
希望这会有所帮助!