我正在尝试围绕无状态 lambda 编写一个模板化包装类。像这样的事情:
template <class TFuncOp>
class Adapter
{
public:
void Op()
{
TFuncOp func; // not possible before C++20
func();
}
};
由于在 C++20 提供默认可构造 lambda 之前这是不可能的,因此我使用此技术来使我的类正常工作:Calling a stateless lambda without an instance (only type)
所以最终的解决方案如下所示:
template <class TFuncOp>
class Adapter
{
public:
static TFuncOp GetOpImpl( TFuncOp *pFunc = 0 )
{
static TFuncOp func = *pFunc;
return func;
}
void Op()
{
GetOpImpl()();
}
};
template <class TFuncOp>
Adapter<TFuncOp> MakeAdapter(TFuncOp func )
{
// Removing the line below has no effect.
//Adapter<TFuncOp>::GetOpImpl( &func );
return Adapter<TFuncOp>();
}
int main()
{
auto adapter = MakeAdapter( [] { printf("Hello World !\n"); } );
adapter.Op();
return 0;
}
此代码适用于所有主要编译器(clang、gcc、msvc)。但有一个令人惊讶的发现。 GetOpImpl() 中 lambda 的静态本地实例的初始化(或缺少初始化)没有效果。无论哪种方式都可以正常工作。
谁能解释一下这是如何工作的?如果我使用 lambda 的静态本地实例而不初始化它,我是否会调用 UB?
最佳答案
无论如何,访问 nullptr
从来都不是一个好主意,因为它是 UB。
但是我们可以看到典型的实现生成的代码可以简单地工作。我尝试解释一下原因:
首先,它与 lambda 无关。这只是在没有数据的类上不需要使用复制构造函数。由于没有数据,生成的代码将不会访问传递的对象。在您的情况下,您“复制”指针 TFuncOp *pFunc = 0 指向的对象,该对象是一个 nullptr,如果必须访问该对象,它将崩溃。由于没有数据可供访问,典型的实现根本不会生成任何访问 nullptr 的代码。但它仍然是UB。
同样的方法适用于所有其他类型,并且 lambda 没有什么特别之处!
struct Empty
{
void Do() { std::cout << "This works the same way" << std::endl; }
// int i; // << if you add some data, you get a seg fault
};
int main()
{
Empty* ptr = nullptr;
Empty empty = *ptr; // get seg fault here, because default copy constructor access the nullptr, but typically only if copy ctor needs to access!
empty.Do();
}
没有捕获数据的 lambda 是一个带有 operator()()
的空结构。
这一切都是一个答案为什么它似乎有效。
关于c++ - 无状态 lambda 作为静态局部变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64178997/