为什么在 VS2019 中 std::initializer_list 的初始化似乎失败了
Why does the initialization of an std::initializer_list seem to fail in VS2019
以下代码在 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;
}
v 的第二次初始化似乎失败了,输出如下:
1 2 3
17824704 10753212 0
但在调试模式下构建或使用其他编译器(gcc、clang)构建时。输出符合预期:
1 2 3
1 2 3
这可能是什么原因?
需要说明的是,v
的唯一 初始化 发生在以下行中:
std::initializer_list<int> v = {};
另外两个是赋值而不是初始化:
v = i;
v = { 1, 2, 3 };
正是这两个作业之间的差异提供了解决方案。
复制初始化列表 不一定 复制底层元素 - 初始化列表通常是轻量级的东西,通常只作为指针和长度实现。
因此,当您将 i
分配给 v
时,i
(因此 v
)的基础数组将继续存在,直到超出范围main
结束。没问题。
当您将 {1, 2, 3}
复制到 v
时,基础数组 也 继续存在,直到它超出范围。不幸的是,一旦赋值完成就会发生这种情况,这意味着在那之后使用 v
的元素将出现问题。
虽然 v
很可能仍然有一个指针和长度,但指针 指向 的东西已经超出范围,并且内存可能已经被重新用于其他用途。
标准 (C++20 [dcl.init.list] /6
) 中的相关文本指出,在谈论初始化列表时:
The [underlying] array has the same lifetime as any other temporary object, except that initializing an initializer_list
object from the array extends the lifetime of the array exactly like binding a reference to a temporary.
由于您所做的不是初始化,因此不会发生此生命周期延长。
这意味着底层数组的破坏被覆盖C++20 [class.temporary] /4
:
Temporary objects are destroyed as the last step in evaluating the full-expression that (lexically) contains the point where they were created.
有趣的是,cplusplus.com site for initializer_list 目前有关于这个 exact 问题的错误代码(我已经向他们提出了这个问题,不确定何时,甚至是否, 他们会修复它):
// 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;
}
虽然这在某些情况下可能有效,但绝不能保证,事实上,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 };
| ^
以下代码在 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;
}
v 的第二次初始化似乎失败了,输出如下:
1 2 3
17824704 10753212 0
但在调试模式下构建或使用其他编译器(gcc、clang)构建时。输出符合预期:
1 2 3
1 2 3
这可能是什么原因?
需要说明的是,v
的唯一 初始化 发生在以下行中:
std::initializer_list<int> v = {};
另外两个是赋值而不是初始化:
v = i;
v = { 1, 2, 3 };
正是这两个作业之间的差异提供了解决方案。
复制初始化列表 不一定 复制底层元素 - 初始化列表通常是轻量级的东西,通常只作为指针和长度实现。
因此,当您将 i
分配给 v
时,i
(因此 v
)的基础数组将继续存在,直到超出范围main
结束。没问题。
当您将 {1, 2, 3}
复制到 v
时,基础数组 也 继续存在,直到它超出范围。不幸的是,一旦赋值完成就会发生这种情况,这意味着在那之后使用 v
的元素将出现问题。
虽然 v
很可能仍然有一个指针和长度,但指针 指向 的东西已经超出范围,并且内存可能已经被重新用于其他用途。
标准 (C++20 [dcl.init.list] /6
) 中的相关文本指出,在谈论初始化列表时:
The [underlying] array has the same lifetime as any other temporary object, except that initializing an
initializer_list
object from the array extends the lifetime of the array exactly like binding a reference to a temporary.
由于您所做的不是初始化,因此不会发生此生命周期延长。
这意味着底层数组的破坏被覆盖C++20 [class.temporary] /4
:
Temporary objects are destroyed as the last step in evaluating the full-expression that (lexically) contains the point where they were created.
有趣的是,cplusplus.com site for initializer_list 目前有关于这个 exact 问题的错误代码(我已经向他们提出了这个问题,不确定何时,甚至是否, 他们会修复它):
// 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;
}
虽然这在某些情况下可能有效,但绝不能保证,事实上,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 };
| ^