包含未初始化值的对象的向量



此代码是否会导致未定义的行为:

#include <vector>
struct S
{
    S() {}
    int x;
};
int main()
{
     std::vector<S> vec(5, S());
}

由于S()默认初始化自动对象,因此其内容不会首先清零,因此x将是不确定的。然后将包含不确定值的对象复制到每个矢量位置。

动机:我们可能期望它的行为与不是 UB 的std::vector<S> vec(5);相同(自 C++11 以来),所以这将是一个容易意外犯的错误。

正如 Praetorian 在评论中提到的,在 C++11 之前,std::vector<S> vec(5);可以自由地执行 5 次默认初始化,或者对部分或全部项目使用复制构造函数。

因此,

考虑到OP的以下评论:

向量 vec(5); 不是 UB(是吗?),我们倾向于认为 (5) 和 (5, S()) 应该做同样的事情。我可以看到这个错误是 很容易意外制造。

问题归结为如果:

vector<S> vec(5);

定义明确,那么为什么是:

std::vector<S> vec(5, S());

未定义的行为?

如果我们转到 std::vector::vector 的 cppreference 部分,我们可以看到在第一种情况下它(自 C++11 以来)(强调我的):

使用计数默认插入的 T 实例构造容器。 不制作副本

而在第二种情况下:

使用具有值的元素的计数副本构造容器 价值。

第一种情况将默认构造元素,并且不会创建副本,而在第二种情况下,将制作副本,因此我们最终将x复制到每个元素。由于 S 的默认构造函数不初始化x,它将具有不确定的值,因此我们有未定义的行为,因为生成不确定值会调用未定义的行为。

自C++14年以来,第128.5节说:

如果评估生成不确定值,则行为 未定义,但以下情况除外

对于在这种情况下不适用的无符号字符,有一些例外情况。

我们知道x在同一段中有一个不确定的值,该段落说:

存储

具有自动或动态存储持续时间的对象时 获得,则对象具有不确定的值,如果没有 对对象执行初始化,该对象保留 不确定的值,直到该值被替换

TL;DR 是的,这是未定义的行为。


在代码中,对临时进行值初始化:

std::vector<S> vec(5, S());
//                    ^^^

从§8.5/10开始:

初始值设定项是一组空括号(即 ())的对象应进行值初始化。

值初始化的定义是:

§8.5/8

对类型为 T 的对象进行值初始化意味着:

— 如果T是一个(可能符合 cv 条件的)类类型(第 9 条),没有默认构造函数 (12.1) 或用户提供或删除的默认构造函数,则对象是默认初始化的;

— [..]

在这种情况下,值初始化不包括零初始化,因为S具有用户声明的默认构造函数。所以它是默认构造的。

§8.5/7

默认初始化类型 T 的对象意味着:

如果T是(可能符合 cv 条件的)类类型(第 9 条),则考虑构造函数。枚举适用的构造函数 (13.3.1.3),并通过重载解析 (13.3) 选择初始值设定项()的最佳构造函数。这样选择的构造函数被调用,使用空参数列表来初始化对象。

— 如果 T 是数组类型,则每个元素都是默认初始化的。

— 否则,不执行初始化。

由于默认构造函数没有显式初始化x,因此它保留了未初始化的垃圾值。默认的复制构造函数使用这些值初始化其他元素,即 UB。

§8.5/12

如果评估生成不确定的值,则行为未定义

最新更新