【问题标题】:Is there any purpose of declaring lambda followed by immediately invocation to initialize local variable using its return value?声明 lambda 然后立即调用以使用其返回值初始化局部变量是否有任何目的?
【发布时间】:2020-04-08 20:51:25
【问题描述】:
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;
    }

由 lambda 的返回值初始化的变量 mt。为什么不直接提取 lambda 代码并使其成为 get_metatype() 函数的一部分,然后只从中返回值呢?这是一些表演技巧还是什么?

此代码来自 decs https://github.com/vblanco20-1/decs 项目,我正在学习用于教育目的。

【问题讨论】:

    标签: c++ templates lambda


    【解决方案1】:

    变量mt被声明为static,意味着在程序或每个翻译单元中对于每个T只有一个mt的实例(具有静态存储时长)(取决于函数是否显示的是类或命名空间范围内的 static 函数),如果函数 get_metatype&lt;T&gt;() 在程序中被多次调用,只有第一次调用到达 mt 的声明将执行 mt 的初始化程序和初始化mt。其他调用将简单地返回 mt 的当时固定值。当涉及多个线程时,这甚至可以保证保持不变,而不会在两个线程中调用初始化程序。

    在初始化程序中使用直接调用的 lambda 允许将所有应该只执行一次以初始化变量 mt 的代码放在一个内联位置,而无需为其创建新函数。如果您将 lambda 中的语句直接放在 get_metatype&lt;T&gt;() 的主体中,那么它们将在函数被调用时执行每次,而不仅仅是一次,并且有责任在初始化中避免数据竞争当涉及多个线程时会转移给你。

    特别是,在这里使用staticmt 的可能原因可能是存在多个线程,并且必须避免构建“元类型”的数据竞争,尽管似乎仍然存在如果针对不同的T 多次调用get_metatype&lt;T&gt;(),则metatype_cache 上的竞争条件甚至数据竞争,因为find 和容器中的放置不是原子完成的。

    或者一个可能的原因是每次调用get_metatype 时对get_metatype 的查找操作执行时间过长,尽管static 变量也不是性能优化的最佳选择,因为它需要线程-safe 检查每个调用以验证mt 是否已经被初始化。

    【讨论】:

      【解决方案2】:

      除了@cigien 所说的 - 静态变量只初始化一次。 C++11 及更高版本确保以线程安全的方式执行初始化。将 lambda 用于初始化程序可确保 lambda 仅被调用一次。如果您要将 lambda 的代码直接内联到 get_metatype(),您将负责自己手动序列化代码,例如使用互斥锁。

      【讨论】:

        【解决方案3】:

        这通常用于初始化需要为const的变量,但初始化起来也很复杂。例如

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

        如果不这样做,就没有好的方法来制作v const

        但是,在您的情况下,在提供的示例中使用此技术的原因是静态变量 mt 不会在每次调用 get_metatype 时被初始化,正如 @walnut 和 @Remylebeau 所指出的.

        【讨论】:

        • @cigien no,mt 本身是一个 non-const 指向 const Metatype 对象的指针,它不是一个 const 指向一个对象。
        • 我编辑了答案以添加静态初始化原因。非常感谢。
        猜你喜欢
        • 1970-01-01
        • 2013-04-19
        • 1970-01-01
        • 2022-01-18
        • 2021-01-07
        • 1970-01-01
        • 1970-01-01
        • 2011-04-15
        • 1970-01-01
        相关资源
        最近更新 更多