是否有声明lambda之后立即调用以使用其返回值初始化局部变量的目的?

template<typename T>
    static const Metatype* get_metatype() {
        static const Metatype* mt = []() {
            constexpr size_t name_hash = Metatype::build_hash<T>().name_hash;

            auto type = metatype_cache.find(name_hash);
            if (type == metatype_cache.end()) {
                constexpr Metatype newtype = Metatype::build<T>();
                metatype_cache[name_hash] = newtype;
            }
            return &metatype_cache[name_hash];
        }();
        return mt;
    }

变量mt由lambda的返回值初始化。为什么不只提取lambda代码并将其作为get_metatype()函数的一部分,然后仅从中返回值呢?这是一些性能技巧吗?

This code from decs https://github.com/vblanco20-1/decs project that i am learning for educational purposes.

评论
  • ni花痴
    ni花痴 回复

    In addition to what @cigien said - a static variable is initialized only once. C++11 and later ensure that initialization is performed in a thread-safe manner. Using a lambda for the initializer ensures that the lambda is called only once. If you were to inline the lambda's code directly in get_metatype(), you would be responsible for manually serializing the code yourself, such as with a mutex.

  • ueaque
    ueaque 回复

    The variable mt is declared static, meaning that there is only one instance (with static storage duration) of mt for each T in the program or in each translation unit (depending on whether the function shown is a static function in a class or at namespace scope) and if the function get_metatype<T>() is called multiple times in the program, only the first call reaching mt's declaration will execute mt's initializer and initialize mt. The other calls will simply return the then-fixed value of mt. This is even guaranteed when multiple threads are involved.

    Using a directly-invoked lambda in the initializer allows one to put all the code that should be executed only once to initialize the variable mt in one place inline without having to create a new function for it. If you put the statements inside the lambda directly in the body of get_metatype<T>(), then they would be executed each time the function is called and not only once, and the responsibility of avoiding data races in the initialization when multiple threads are involved would shift to you.

    In particular, possible reasons for using the static mt here might be that there are multiple threads and a data race on the access metatype_cache or Metatype::build<T> must be avoided or a possible reason could be that the lookup operations into get_metatype take too long to execute each time get_metatype is called, although a static variable is not the best choice for performance optimization either, since it requires thread-safe checks on each call to verify whether mt has already been initialized.

  • 波半梅
    波半梅 回复

    This is commonly used to initialize a variable that needs to be const, but is also complex to initialize. e.g.

    const std::vector<int> v = [] {
       std::vector<int> vv;
       // ... very complicated logic to initialize vv
       // even could be IO, etc ...
       return vv;
    }();
    

    without doing this, there's no good way to make v const.

    However, in your case, the reason for using this technique in the provided example, is so that the static variable mt is not initalized on every call to get_metatype, as pointed out by @walnut, and @Remylebeau.