首页 > 解决方案 > GCC 对可能有效的代码抛出 init-list-lifetime 警告?

问题描述

我在 Debian 不稳定的 GCC 9.3.0 上运行。

我从事的一个项目最近发生了一个变化,该项目引入了类似于下面的代码。

#include <initializer_list>
#include <map>
#include <vector>

std::map<int, std::vector<int>> ex = []{
    /* for reused lists */
    std::initializer_list<int> module_options;

    return (decltype(ex)) {
        {1, module_options = {
            1, 2, 3
        }},
        {2, module_options},
    };
}();

这个想法是初始化列表的相同子部分首先在顶部声明,std:initializer_list在第一次使用时定义并分配给变量,然后在多个地方使用。这很方便,有些人可能会认为更具可读性,这就是它被接受的原因。

一切都很好,直到几天前 GCC 开始init-list-lifetime对代码发出警告。我们-Werror在回归中使用,所以这对我来说失败了。我还尝试使用 clang 9.0.1 进行编译,它不会引发警告。

<source>: In lambda function:
<source>:12:9: warning: assignment from temporary 'initializer_list' does not extend the lifetime of the underlying array [-Winit-list-lifetime]
   12 |         }},
      |         ^

根据cppreference

在原始初始化列表对象的生命周期结束后,不能保证底层数组存在。std::initializer_list 的存储是未指定的(即它可以是自动的、临时的或静态的只读存储器,具体取决于情况)。

所以我的理解是,在包含初始化程序列表的范围内定义的通用初始化程序列表值具有以封闭初始化程序列表结束的生命周期。从前面的 cppreference 页面中,它提到这std::initializer_list是一个“轻量级代理对象”,这意味着它不获取临时对象的所有权或延长它的生命周期。这意味着底层数组不能保证在以后的使用中存在,这就是抛出警告的原因。这个分析正确吗?

std::initializer_list我可以通过将变量初始化移动到声明中来防止发生警告。有关项目中问题的完整详细信息,请参阅PR

标签: c++c++11gcclanguage-lawyer

解决方案


所以我的理解是,在包含初始化程序列表的范围内定义的通用初始化程序列表值具有以封闭初始化程序列表结束的生命周期

你说的是prvalue表达式创建的对象{1, 2, 3},对吧?

decl.init.list/6中有一个例子,

该数组与任何其他临时对象 ([ class.temporary ])具有相同的生命周期,除了从数组初始化initializer_­list对象会延长数组的生命周期,就像将引用绑定到临时对象一样。[示例:

// ...
std::initializer_list<int> i3 = { 1, 2, 3 };
// ...

其中标准(或草案)说

对于i3initializer_­list对象是一个变量,因此该数组在变量的生命周期内保持不变。

这表明该对象必须被物化并且应该延长其生命周期。

但是,您没有从表达式初始化initializer_list对象,因为您的变量已经初始化。如果我们将您的代码重写为对名义上的调用

module_options.operator=({1, 2, 3})

那么我们不会期望临时生命周期延长到函数调用结束之后。

我曾怀疑这个临时的仍然会活到完整表达式的末尾,因为我认为绑定一个引用应该延长它的生命周期而不是减少它:但不可否认class.temporary/6“......持续引用的生命周期......”而不是“......至少持续生命周期......”

但是,这确实意味着原始代码的以下变体应该可以满足您的需求:

std::map<int, std::vector<int>> ex = []{
    /* for reused lists */
    std::initializer_list<int> module_options { 1, 2, 3 };

    return (decltype(ex)) {
        {1, module_options},
        {2, module_options},
    };
}();

推荐阅读