你的文章仍然一团糟,同一期有4个不同版本。我将关注第一个代码片段,因为它似乎是最接近[mcve]的代码片段,我将澄清如何正确使用lambda和函数对象。
int AddToInt(int func(int output))
这有点误导人。我建议将其更改为等效的,但更多地用于:
int AddToInt(int (*func)(int))
这意味着:声明一个名为
AddToInt
其中:
-
接受“指向接受int并返回int的函数的指针”类型的参数,并且
-
返回一个
int
.
如您所见,您的函数接受一个经典的C函数指针。它不会接受任何类型的函数对象。这里要注意的是,没有捕获的lambda可以转换为函数指针。
如保留上述声明:
AddToInt([](int b) { return b + 1; }); // ok, non-capturing lambda conversion to function pointer
AddToInt([a](int b) { return a + b; }); // error cannot convert capturing lambda to function pointer
原因很容易理解。非捕获lambda可以等效于自由函数,但捕获lambda具有状态(由捕获集形成),因此它比简单的、经典的自由函数“更多”。
正如您所看到的,接受函数指针在很大程度上是一个古老的习语,因为这些限制(甚至不考虑传递任何类型的函数对象,例如
operator()
定义的)。
对于接受任何类型的可调用对象,通常有两个选项:通用模板或标准
std::function
对象。
模板对象
template <class Fn>
int AddToInt1(Fn func)
{
return func(3);
}
现在你可以打电话了
AddToInt1
任何类型的呼叫。取决于推导的类型
Fn
类型可以使用此方法获得零开销。缺点是您可以接受任何类型,包括不可调用的类型,或者参数或返回类型不正确的类型。概念将减轻这些不利因素的大部分。
AddToInt1([](int b) { return b + 1; }); // OK
AddToInt1([a](int b) { return a + b; }); // OK
您还可能希望添加完美的转发(为了简洁起见,在示例中省略了)。
STD::功能
另一条路线是
STD::功能
:
int AddToInt2(std::function<int(int)> func)
这里的缺点是
STD::功能
对象。它使用类型擦除,这会增加大量的性能损失(根据您的使用情况,这是完全可以接受的)。
AddToInt2([](int b) { return b + 1; }); // OK
AddToInt2([a](int b) { return a + b; }); // OK
现在,一旦您了解了上面的要点,您的代码还有一些问题需要解决:
[a] (int b) { a += b;};
首先,您知道这个lambda不会返回任何内容吗?此外,它还尝试修改由值捕获的
a
这是非法的,因为lambda的
运算符()
是
const
默认情况下是有充分理由的。如果希望lambda修改外部
一
捕获变量,然后需要通过引用捕获它:
[&a] (int b) { a += b;};
现在你必须非常小心不要以一个悬而未决的参考结束。
但我怀疑你的意思是:
AddToInt([a] (int b) { return a + b;});
但这纯粹是我的猜测。
下面是一个完全有效的例子:
template <class Fn>
int AddToInt1(Fn func)
{
return func(3);
}
int AddToInt2(std::function<int (int)> func)
{
return func(3);
}
int GetCoolInt()
{
int a = 3;
return AddToInt1([a] (int b) { return a + b;}); // OK
//return AddToInt2([a] (int b) { return a + b;}); // OK
}
这里有一些我刚刚提到的重要的观点,但是对它们的详细阐述就相当于编写一个关于lambdas及更高版本的完整教程,这超出了本网站的范围。总之,你必须自己研究这个课题。