为什么在 VS2019 中 std::initializer_list 的初始化似乎失败

1 c++ initializer-list visual-studio

以下代码在以 Release 模式编译的 Visual Studio 2019 上失败。

#include <iostream>
#include <iterator>
#include <initializer_list>

int main( int, char** )
{
    std::initializer_list<int> v = {};
    std::initializer_list<int> i = { 1, 2, 3 };

    v = i;
    std::copy( v.begin(), v.end(), std::ostream_iterator<int>( std::cout, " " ) );
    std::cout << std::endl;

    v = { 1, 2, 3 };
    std::copy( v.begin(), v.end(), std::ostream_iterator<int>( std::cout, " " ) );
    std::cout << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

v 的第二次初始化似乎失败了,输出如下:

1 2 3
17824704 10753212 0
Run Code Online (Sandbox Code Playgroud)

但是在调试模式下构建或使用其他编译器(gcc、clang)构建时。输出如预期:

1 2 3
1 2 3
Run Code Online (Sandbox Code Playgroud)

这可能是什么原因?

pax*_*blo 6

只要是明确的,唯一的初始化v发生在行:

std::initializer_list<int> v = {};
Run Code Online (Sandbox Code Playgroud)

另外两个是赋值而不是初始化:

v = i;
v = { 1, 2, 3 };
Run Code Online (Sandbox Code Playgroud)

正是这两个任务之间的差异提供了解决方案。

复制一个初始化器列表确实不是一味模仿底层的元素-初始化器列表通常是一个轻量级的东西,只是一个指针和长度经常实施。

所以,当你分配,iv,对底层数组i(因此v)继续存在,直到结束的范围走出去main。没有问题。

当您复制{1, 2, 3}到 时v,底层数组也会继续存在,直到它超出范围。不幸的是,这会在赋值完成后立即发生,这意味着在那之后使用 的元素v将是有问题的。

虽然v最有可能仍将有一个指针和长度,事情指针指向已经超出范围,和记忆可能已经别的东西再利用。


C++20 [dcl.init.list] /6在谈论初始化列表时,标准 ( ) 中的相关文本指出:

[底层] 数组与任何其他临时对象具有相同的生命周期,除了initializer_list从数组初始化对象会延长数组的生命周期,就像将引用绑定到临时对象一样。

由于您所做的不是初始化,因此不会发生此生命周期延长。

这意味着底层数组的破坏由C++20 [class.temporary] /4

临时对象被销毁作为评估(词汇上)包含它们创建点的完整表达式的最后一步。


有趣的是,用于 initializer_listcplusplus.com 站点上目前有关于这个确切问题的错误代码(我已经向他们提出了这个问题,不确定何时,甚至他们是否会修复它):

// initializer_list example
#include <iostream>          // std::cout
#include <initializer_list>  // std::initializer_list

int main ()
{
    std::initializer_list<int> mylist;
    mylist = { 10, 20, 30 };
    std::cout << "mylist contains:";
    for (int x: mylist) std::cout << ' ' << x;
    std::cout << '\n';
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

虽然这在某些情况下可能有效,但并不能保证,事实上,gcc 10.2(在Compiler Explorer上检查)正确地将其视为有问题:

<source>: In function 'int main()':
<source>:8:25: warning: assignment from temporary 'initializer_list'
    does not extend the lifetime of the underlying array
    [-Winit-list-lifetime]
8 |   mylist = { 10, 20, 30 };
  |                         ^
Run Code Online (Sandbox Code Playgroud)

  • @JoVanderSnickt 的区别在于初始化延长了临时数组的生命周期(与引用类似),但赋值却没有。例如,请参阅以下相关问题:/sf/ask/1070051531/ 和 /sf/ask/4401416211/。人们可能想知道为什么“std::initializer_list”甚至提供赋值运算符。 (2认同)