在C ++中,可以分离函数的声明和定义。例如,声明一个函数是很正常的:
int Foo(int x);
<{1>}中的并在Foo.h
中实施。是否有可能与lambdas做类似的事情?例如,定义一个
Foo.cpp
在std::function<int(int)> bar;
中并在bar.h
中将其实现为:
bar.cpp
免责声明:我在C#中有使用lambdas的经验,但我没有在C ++中使用它们。
答案 0 :(得分:39)
您不能将lambdas的声明和定义分开,也不能向前声明它。它的类型是一个未命名的闭包类型,它使用lambda表达式声明。但是你可以用std::function个对象做到这一点,它被设计为能够存储任何可调用的目标,包括lambda表达式。
如您所示的示例代码一直在使用std::function
,请注意本案例bar
确实是一个全局变量,您需要在头文件中使用extern
来制作它是一个声明(不是定义)。
// bar.h
extern std::function<int(int)> bar; // declaration
和
// bar.cpp
std::function<int(int)> bar = [](int n) // definition
{
if (n >= 5) return n;
return n*(n + 1);
};
再次注意,这不是lambda的单独声明和定义;它只是单独声明和定义类型为bar
的全局变量std::function<int(int)>
,它是从lambda表达式初始化的。
答案 1 :(得分:8)
严格来说,你不能
lambda表达式是一个prvalue表达式,其值为(直到 C ++ 17)其结果对象是(从C ++ 17开始)一个未命名的临时对象 唯一的非命名非联合非聚合类类型,称为闭包 type,在最小的范围内声明(出于ADL的目的) 块范围,类范围或包含lambda的命名空间范围 表达
因此lambda是未命名的临时对象。您可以将lambda绑定到l值对象(例如std::function
),并通过有关变量声明的常规规则,您可以将声明和定义分开。
答案 2 :(得分:6)
前向声明不是正确的术语,因为C ++中的lambda是对象,而不是函数。代码:
std::function<int(int)> bar;
声明一个变量,你不必强制分配它(该类型的默认值为“指向无函数的指针”)。你甚至可以编译它的调用...例如代码:
#include <functional>
#include <iostream>
int main(int argc, const char *argv[]) {
std::function<int(int)> bar;
std::cout << bar(21) << "\n";
return 0;
}
将干净地编译(但当然会在运行时疯狂地表现)。
那就是说你可以将lambda分配给兼容的std::function
变量并添加例如:
bar = [](int x){ return x*2; };
在调用之前将导致编译良好的程序并生成输出42。
一些非常明显的事情可能会令人惊讶,因为C ++中的lambdas(如果你知道其他有这个概念的语言)就是那个
每个lambda [..](...){...}
都有不同的不兼容类型,即使签名完全相同。例如,您无法声明lambda类型的参数,因为唯一的方法是使用类似decltype([] ...)
之类的东西但是然后就无法像调用站点上的任何其他[]...
形式一样调用该函数是不相容的。这由std::function
解决,因此如果你必须传递lambdas或将它们存储在容器中,你必须使用std::function
。
Lambdas可以按值捕获本地(但它们是const
除非您声明lambda mutable
)或通过引用(但保证引用对象的生命周期不会短于lambda的生命周期取决于程序员)。 C ++没有垃圾收集器,这是正确解决“向上funarg”问题所需的东西(你可以通过捕获智能指针来解决,但你必须注意引用循环以避免泄漏)。
与其他语言不同,lambda可以被复制,当你复制它们时,你会拍摄其内部捕获的值变量的快照。对于可变状态而言,这可能是非常令人惊讶的,我认为默认情况下,按值值捕获的值为const
。
合理化并记住有关lambdas的许多细节的方法是代码:
std::function<int(int)> timesK(int k) {
return [k](int x){ return x*k; };
}
基本上就像
std::function<int(int)> timesK(int k) {
struct __Lambda6502 {
int k;
__Lambda6502(int k) : k(k) {}
int operator()(int x) {
return x * k;
}
};
return __Lambda6502(k);
}
有一个细微差别,甚至可以复制lambda捕获引用(通常包含引用的类作为成员不能)。