隐式构造函数中未检查 C++11 初始值设定项列表长度
C++11 initializer list length is not checked in implicit constructor
我发现当一个带有默认构造函数的简单数据结构包含一个数组时,可以使用不同数量的参数调用默认构造函数,即:
struct LayerData
{
uint32_t d[60];
};
可以通过以下方式初始化:
LayerData in({rand(),rand(),rand(),rand(),rand()});
并且编译正确。
这是 C++11 中的预期行为吗?隐式构造函数中是否没有编译时大小检查?
有编译时检查。这不会编译:
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
数组初始化时所包含的项目可能少于其包含的项目。在这种情况下,剩余的元素被值初始化(即零)。
N3337 8.5.1/7
If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member
not explicitly initialized shall be initialized from an empty initializer list (8.5.4).
struct S { int a; const char* b; int c; };
S ss = { 1, "asdf" };
initializes ss.a with 1, ss.b with "asdf", and ss.c with the value of an expression of the form int(),
that is, 0.
因此在您的示例中,前 5 个元素用 rand()
初始化,其他元素用 int()
初始化,即 0
。
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."
}
仓促写的,不过你应该明白了。
我发现当一个带有默认构造函数的简单数据结构包含一个数组时,可以使用不同数量的参数调用默认构造函数,即:
struct LayerData
{
uint32_t d[60];
};
可以通过以下方式初始化:
LayerData in({rand(),rand(),rand(),rand(),rand()});
并且编译正确。
这是 C++11 中的预期行为吗?隐式构造函数中是否没有编译时大小检查?
有编译时检查。这不会编译:
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
数组初始化时所包含的项目可能少于其包含的项目。在这种情况下,剩余的元素被值初始化(即零)。
N3337 8.5.1/7
If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member not explicitly initialized shall be initialized from an empty initializer list (8.5.4).
struct S { int a; const char* b; int c; };
S ss = { 1, "asdf" };
initializes ss.a with 1, ss.b with "asdf", and ss.c with the value of an expression of the form int(), that is, 0.
因此在您的示例中,前 5 个元素用 rand()
初始化,其他元素用 int()
初始化,即 0
。
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."
}
仓促写的,不过你应该明白了。