【发布时间】:2016-12-25 19:20:52
【问题描述】:
我一直认为,临时对象会一直存在到完整表达式的末尾。然而,std::vector 和数组的初始化之间有一个奇怪的区别。
请考虑以下代码:
#include <iostream>
#include <vector>
struct ID{
static int cnt;
// the number of living object of class ID at the moment of creation:
int id;
ID():id(++cnt){}
~ID(){
cnt--;
}
};
int ID::cnt=0;
int main(){
int arr[]{ID().id, ID().id};
std::vector<int> vec{ID().id, ID().id};
std::cout<<" Array: "<<arr[0]<<", "<<arr[1]<<"\n";
std::cout<<" Vector: "<<vec[0]<<", "<<vec[1]<<"\n";
}
这个程序的输出有点(至少对我来说)出乎意料:
Array: 1, 1
Vector: 1, 2
这意味着,在std::vector 的整个初始化过程中,临时对象都是活动的,但在数组的情况下,它们会一个接一个地创建和销毁。我希望临时人员能够活到完整的表达式 int arr[]{ID().id, ID().id}; 完成。
该标准提到了一个关于临时对象的生命周期和数组初始化的例外 (12.2)。但是我不明白它的含义,也不知道为什么在这种特殊情况下应用它:
临时对象在两种情况下被销毁 与完整表达式的结尾不同的点。第一个上下文 是调用默认构造函数来初始化 大批。如果构造函数有一个或多个默认参数,则 在默认参数中创建的每个临时对象的销毁是 在构造下一个数组元素(如果有)之前排序。
使用不同编译器的结果概述(MSVS 结果是 NathanOliver 的屈从):
Array Vector
clang 3.8 1, 2 1, 2
g++ 6.1 1, 1 1, 2
icpc 16 1, 1 1, 2
MSVS 2015 1, 1 1, 2
正如 ecatmur 所指出的,对于聚合初始化,braced-init-list 的每个元素都是一个完整表达式,因此以下代码
struct S{
int a;
int b;
} s{ID().id, ID().id};
std::cout<<" Struct: "<<s.a<<", "<<s.b<<"\n";
应该将Struct 1, 1 打印到控制台。这正是 g++ 编译的程序所做的。但是,clang 似乎有一个错误 - 生成的程序打印 Struct 1, 2。
已向 clang 报告了一个错误:https://llvm.org/bugs/show_bug.cgi?id=29080
【问题讨论】:
-
arr使用聚合初始化,vec使用构造函数调用。 -
似乎是 gcc 的一个错误,clang 为 Demo 提供了
1, 2 -
公平地说,这里的完整表达式是“ID().id”。然而在向量的情况下,最外层的表达式是对初始化列表构造函数的构造函数调用。
auto &&arr2 = decltype(arr){ID().id, ID().id};会发生什么? -
FWIW MSVS 2015 更新 3 给出与 g++ 相同的结果。
-
@JohannesSchaub-litb
auto &&arr2 = decltype(arr){ID().id, ID().id};如果使用 g++ 编译,则结果为{1,1}