c++templateslambdaiife

Is there any purpose of declaring lambda followed by immediately invocation to initialize local variable using its return value?


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

Variable mt initialized by lambda's return value. Why not just extract lambda code and make it part of get_metatype() function then just return value from it? Is this some performance trick or what??

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


Solution

  • 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 to hold when multiple threads are involved, without the initializer ever being called in two of the threads.

    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 construction of the the "metatype" must be avoided, although there would still seem to be a race condition or even a data race on metatype_cache if multiple calls to get_metatype<T>() for different T are made, because the find and the placement into the container are not done atomically.

    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.