我在一个类中有一个静态lambda,它在头文件中声明和定义为:
class A final {
// inline could be removed of course for splitting
static inline auto foo = [](auto& param1, auto& param2 auto& param3) {
// do stuff
return;
}
}
// compiles fine
使用static int x = 42
等静态变量,我可以像这样拆分声明和定义:
// bar.h:
class Bar {
static int x;
}
// bar.cpp:
#include "bar.h"
int Bar::x = 42;
如何用上述lambda来达到同样的目的?当然,改变签名是可以的。
基本问题是每个lambda表达式都有自己独立的类型(see also this answer)。由于您需要知道声明变量的类型,并且您需要知道lambda表达式以了解其类型,因此无法在不知道lambda表达式本身的情况下声明变量来保存lambda表达式的结果。
只要知道两个地方的lambda表达式,就可以声明一个变量来保存lambda并单独定义变量。例如:
inline auto makeFoo() {
return [](auto& param1, auto& param2, auto& param3) {
// do stuff
return;
};
}
class A final {
static decltype(makeFoo()) foo;
};
decltype(makeFoo()) A::foo = makeFoo();
但是,不可能将变量的声明与lambda表达式分开(即,只能在放置变量定义的文件中使用lambda表达式)。
不捕获任何内容的lambda表达式可以转换为指向函数的指针。如果你不需要你的lambda捕获任何东西(比如在你的例子中)并且只需要一个特定签名的可调用,你可以简单地声明A::foo
是函数指针类型并用匹配的lambda初始化A::foo
的定义:
class A final {
static void (*foo)(int&, float&, double&);
};
void (*A::foo)(int&, float&, double&) = [](auto& param1, auto& param2, auto& param3) {
// do stuff
return;
};
如果你真的需要一个普通的lambda(一个带有auto
参数),就像你的例子所建议的那样,那也不会有用,而且你很可能运气不好。使用泛型调用运算符意味着闭包类型的operator ()
函数必须是函数模板。拥有operator ()
函数模板意味着必须知道其定义才能让任何人实际拨打电话。即使您编写自己的类而不是使用lambda表达式,也无法让任何人在不知道其定义的情况下调用泛型operator ()
。您所能做的就是为所有需要支持的签名声明operator ()
模板的显式实例化,并单独定义它们。但是,再一次,要求你事先确实知道你需要哪些具体签名才能支持你的...
如何用上述lambda来达到同样的目的?
你不能。您必须始终声明变量的类型。如果在声明变量之后定义lambda,则无法从初始化程序推断出声明的类型。但是,在定义lambda之前,你不能引用它的类型,因为类型是匿名的。
您可以简单地使用命名类型的函数对象,而不是lambda(即匿名函数对象)。然后,您可以拆分函数定义。此外,您必须声明函数的返回类型,因为如果没有函数的定义,则无法推断它:
class A final {
constexpr struct Foo {
template<class Param1, class Param2, class Param3>
void operator()(Param1&, Param2&, Param3&) const;
} foo{};
};
但是,正如您可能注意到的,该函数实际上是一个函数模板。这是因为你在lambda中使用auto
参数。如果要在标头外定义模板,则必须将实例化约束为有限集,并显式实例化定义模板的模板:
// the definition
template<class Param1, class Param2, class Param3>
void A::Foo::operator()(Param1&, Param2&, Param3&) const {
// do stuff
return;
}
// explicit instantiations
template void A::Foo::operator()<int, int, int>(int&, int&, int&) const;
template void A::Foo::operator()<int, double, float>(int&, double&, float&) const;
如果尝试使用尚未实例化的参数进行调用,则在未定义模板的转换单元中,将出现链接器错误。
如果你希望保持参数不受约束,那么你就会有相互冲突的要求。只能通过在标题中定义模板来实现无约束的模板。
另一方面,您可能想要首先考虑是否需要函数对象。你没有证明你需要它。如果您要将其设置为静态成员函数(模板)而不是函数对象,则上述示例也可以正常工作。
你仍然可以用旧的方式创建仿函数:
struct Foo
{
template <typename T1, typename T2, typename T3>
void operator ()(T1& param1, T2& param2, T3& param3) const;
};
template <typename T1, typename T2, typename T3>
void Foo::operator ()(T1& param1, T2& param2, T3& param3) const
{
/*..*/
}
class A final {
// inline could be removed of course for splitting
static const Foo foo;
};
// in .cpp
const Foo A::foo{};