简体   繁体   English

无状态 lambdas 作为静态局部变量

[英]Stateless lambdas as static local variable

I am trying to write a templated wrapper class around a stateless lambda.我正在尝试围绕无状态 lambda 编写模板化包装类。 Something like this:像这样的东西:

template <class TFuncOp>
class Adapter
{
public:
    void Op()
    {
        TFuncOp func; // not possible before C++20
        func();
    }
};

Since this isn't possible before default constructible lambdas arrive with C++20, I used this technique to make my class work: Calling a stateless lambda without an instance (only type)由于在 C++20 的默认可构造 lambdas 到来之前这是不可能的,我使用这种技术使我的类工作: 在没有实例的情况下调用无状态 lambda(仅类型)

So the final solution looks like this:所以最终的解决方案是这样的:

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;
}

This code works on all major compilers (clang, gcc, msvc).此代码适用于所有主要编译器(clang、gcc、msvc)。 But with one surprising discovery.但有一个令人惊讶的发现。 Initialization (or lack thereof) of the static local instance of the lambda in GetOpImpl() has no effect. GetOpImpl() 中 lambda 的静态本地实例的初始化(或缺少初始化)不起作用。 It works fine either way.无论哪种方式,它都可以正常工作。

Can anyone explain how this works?谁能解释一下这是如何工作的? Am I invoking UB if I use the static local instance of the lambda without initializing it?如果我使用 lambda 的静态本地实例而不初始化它,我是否会调用 UB?

In any case, accessing a nullptr is never a good idea as it is UB.在任何情况下,访问nullptr都不是一个好主意,因为它是 UB。

But we can see that typical implementations generate code which simply works.但是我们可以看到典型的实现生成的代码很简单。 I try to explain why:我试着解释原因:

First, it has nothing to do with lambdas.首先,它与 lambdas 无关。 It is simply the not needed using of a copy constructor on a class which has no data.它只是不需要在没有数据的类上使用复制构造函数。 As you have no data, the generated code will not access the passed object.由于您没有数据,生成的代码将不会访问传递的对象。 In your case, you "copy" the object which the pointer TFuncOp *pFunc = 0 points to, which is a nullptr which will crash if the object must be accessed.在您的情况下,您“复制”了指针TFuncOp *pFunc = 0指向的对象,这是一个 nullptr,如果必须访问该对象,它将崩溃。 As there is no data to access, a typical implementation will not genrate any code which will access the nullptr at all.由于没有要访问的数据,典型的实现根本不会生成任何访问 nullptr 的代码。 But it is still UB.但它仍然是UB。

The same works with all other types in the same way and has nothing special with a lambda!以相同的方式适用于所有其他类型,并且对于 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();
}

And a lambda which has no captured data, is an empty structure with a operator()() .没有捕获数据的 lambda 是一个带有operator()()的空结构。

That all is a answer why it seems to work.这就是为什么它似乎有效的答案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM