我在类中有一个静态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归档同一件事?当然,更改签名是可以的。
答案 0 :(得分:3)
基本问题是每个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
为函数指针类型并初始化{{1 }}和匹配的lambda:
A::foo
如果您确实需要示例所建议的通用lambda(带有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 ()
函数模板意味着任何人都必须知道其定义才能实际进行调用。即使您编写自己的类而不使用lambda表达式,也无法让任何人在不知道其定义的情况下调用泛型operator ()
。您所要做的就是为需要支持的所有签名声明operator ()
模板的显式实例化,并分别定义它们。但这又需要您实际上预先知道需要哪些可调用的具体签名才能支持...
答案 1 :(得分:2)
如何用上述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;
如果您尝试使用未实例化的参数进行调用,则在未定义模板的翻译单元中,将出现链接器错误。
如果您希望不限制参数,那么您的要求就有冲突。不受约束的模板只能通过在标头中定义模板来实现。
另一方面,您可能首先要考虑是否需要一个功能对象。您尚未证明您需要它。如果您将其设为静态成员函数(模板)而不是函数对象,则上述示例同样适用。
答案 2 :(得分:1)
您仍然可以使用旧方法创建函子:
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{};