GCC throws init-list-lifetime warning on potentially valid code?

倾然丶 夕夏残阳落幕 提交于 2021-02-20 10:15:10

问题


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

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.

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 |         }},
      |         ^

According to cppreference:

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).

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?

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.


回答1:


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

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

There's an example in decl.init.list/6,

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

of which the standard (or draft) says

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.

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.

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


来源:https://stackoverflow.com/questions/62877374/gcc-throws-init-list-lifetime-warning-on-potentially-valid-code

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!