在隐式构造函数中未检查C++11初始值设定项列表长度

C++11 initializer list length is not checked in implicit constructor

本文关键字:列表 构造函数 C++11 检查      更新时间:2023-10-16

我发现,当一个具有默认构造函数的简单数据结构包含一个数组时,可以使用不同数量的参数调用默认构造函数,即:

struct LayerData
{
    uint32_t d[60];
};

可通过以下方式初始化:

LayerData in({rand(),rand(),rand(),rand(),rand()});

而且它编译得很好。

这是C++11中预期的行为吗?隐式构造函数中是否没有编译时大小检查?

N3337 8.5.1/7

如果列表中的初始值设定项子句少于聚合中的成员,则每个成员未显式初始化的应从空初始化器列表中初始化(8.5.4)。

struct S { int a; const char* b; int c; };
S ss = { 1, "asdf" };

用1初始化ss.a,用"asdf"初始化ss.b,用形式为int()的表达式的值初始化ss.c,即0

因此,在您的示例中,前5个元素用rand()初始化,另一个用int()初始化,即0

有编译时检查。这不会编译:

struct A
{
  int b[3];
};
int main()
{
  A a { 1, 2 };       // no problem here. equivalent to 1, 2, 0
  A b { 1, 2, 3, 4 }; // problem here. too many initializers
}

因为:

/tmp/gcc-explorer-compiler11648-73-eh15v1/example.cpp: In function 'int main()':
10 : error: too many initializers for 'A'
A b { 1, 2, 3, 4 };
^
Compilation failed

数组初始化时可以使用比它包含的项更少的项。在这种情况下,剩余元素被值初始化(即零)。

GCC 4.8.4将对代码进行精细编译。并不是每个编译器都这样做,MSVC++14(VS 2015)并没有编译

LayerData in({rand(),rand(),rand(),rand(),rand()});

但它确实编译了

LayerData in{{rand(),rand(),rand(),rand(),rand()}};

使用C++11通用表示法

看看GCC允许这样做的原因,似乎是因为它在堆栈上分配类型,然后使用XMM一次将32位(针对x64编译时为64位)移动到内存位置。也许这会随着类型的大小而改变,但通常我会说,如果你需要强制执行相同的长度,你无论如何都不应该像这样暴露类型。请注意,通用表示法实际上可以对您有利,使用模板,您可以形成看起来与您尝试做的非常相似的东西,并强制执行相同数量的参数:

#include <cstdint>
template <int N>
class LayerData
{
    static_assert(N > 0, "Number must be greater than 0");
public:
    uint32_t d[N];
    template<typename... Args>
    LayerData(Args&&... args) : d{uint32_t(args)...}
    {
        static_assert(sizeof...(Args) == N, "Invalid number of constructor arguments.");
    }
};
int main()
{
    LayerData<4> in1{1, 2, 3, 4}; //Fine
    LayerData<60> in2{1, 2, 3, 4}; //Causes error "Invalid number of constructor arguments."
}

写得很匆忙,但你应该明白。