GCC对可能有效的代码引发init-list-lifetime警告吗? [英] GCC throws init-list-lifetime warning on potentially valid code?

查看:165
本文介绍了GCC对可能有效的代码引发init-list-lifetime警告吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用GCC 9.3.0在Debian上运行不稳定.

I'm running on Debian unstable with GCC 9.3.0.

我从事的一个项目的最新更改与下面的代码类似.

There was a recent change on a project I work on that introduced code similar to what's below.

#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 变量,然后在多个地方使用.这很方便,而且有些人可能认为它更具可读性,这就是为什么它被接受的原因.

The idea is that identical subsections of the initializer lists are first declared at the top, defined and assigned to the std:initializer_list variable at the first usage, then used in multiple places. This is convenient, and some may argue more readable, which is why it was accepted.

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

All was well until a few days ago where GCC started throwing a init-list-lifetime warning on the code. We use -Werror in our regression, so this fails the regression for me. I also tried compiling with clang 9.0.1, which does not throw the warning.

<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的存储(即取决于情况,它可以是自动,临时或静态只读存储器).

The underlying array is not guaranteed to exist after the lifetime of the original initializer list object has ended. The storage for std::initializer_list is unspecified (i.e. it could be automatic, temporary, or static read-only memory, depending on the situation).

因此,我的理解是,在包含初始化器列表的范围内定义的公共初始化器列表值的生命周期以封闭的初始化器列表结尾.在前面的cppreference页面中,它提到 std :: initializer_list 是一个轻量级代理对象",这意味着它不拥有临时对象的所有权或延长其寿命.这意味着不能保证基础数组在以后的使用中存在,这就是引发警告的原因.这种分析正确吗?

So my understanding is that the common initializer list value, being defined within the scope of an encompassing initializer list, has a lifetime that ends with the enclosing initializer list. From the cppreference page earlier, it mentions that std::initializer_list is a "lightweight proxy-object", which implies that it does not take ownership of the temporary object or extend it's lifetime. This means that the underlying array is not guaranteed to exist in later usage, which is why the warning is being thrown. Is this analysis correct?

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

I can prevent the warning from occuring by moving the std::initializer_list variable initialization to the declaration. For full details on the problem as it stands in the project see the PR.

推荐答案

因此,我的理解是,在包含初始值设定项列表的范围内定义的通用初始值设定项列表值的生命周期以封闭的初始值设定项列表结尾

So my understanding is that the common initializer list value, being defined within the scope of an encompassing initializer list, has a lifetime that ends with the enclosing initializer list

您正在谈论由prvalue表达式 {1、2、3} 创建的对象,对吗?

You're talking about the object created by the prvalue expression {1, 2, 3}, right?

decl.init.list/6 ,

该数组与其他任何临时对象([ class.temporary ]),除了从数组初始化 initializer_list 对象可以延长数组的生存期,就像将引用绑定到临时对象一样.[示例:

The array has the same lifetime as any other temporary object ([class.temporary]), except that initializing an initializer_­list object from the array extends the lifetime of the array exactly like binding a reference to a temporary. [Example:

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

标准(或草案)说的

对于 i3 initializer_list 对象是一个变量,因此该数组在变量的整个生命周期内都保持不变.

For i3, the initializer_­list object is a variable, so the array persists for the lifetime of the variable.

这表明该对象必须实现,并且应延长其寿命.

This suggests the object must be materialized and should have its lifetime extended.

但是,您不是从表达式中初始化 initializer_list 对象,因为您的变量已被初始化.如果我们将您的代码重写为对概念的调用

However, you are not initializing the initializer_list object from the expression, because your variable is already initialized. If we rewrote your code as a call to a notional

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

那么我们就不会期望临时寿命会在函数调用结束后延长.

then we wouldn't expect the temporary lifetime to be extended past the end of the function call.

我曾怀疑临时对象是否还会活到完整表达式的末尾,因为我认为绑定对它的引用应该延长而不是减少它的寿命:但是我承认 class.temporary/6 说".引用..." 而不是" ...的生存期持续至少生存期..."

I had suspected the temporary would still live to the end of the full-expression, since I thought that binding a reference to was supposed to extend its lifetime rather than reduce it: but admittedly class.temporary/6 says "... persists for the lifetime of the reference ..." and not "... persists for at least the lifetime ..."

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

However, it does mean that the following variation of your original code should do what you want:

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},
    };
}();

这篇关于GCC对可能有效的代码引发init-list-lifetime警告吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆