考虑代码:
struct Foo
{
const char str[] = "test";
};
int main()
{
Foo foo;
}
无法使用g ++和clang ++编译,基本上是吐出
error: array bound cannot be deduced from an in-class initializer
我明白这是标准可能会说的,但有什么特别好的理由吗?由于我们有一个字符串文字,似乎编译器应该能够毫无问题地推断出大小,类似于你只是声明一个类外的const
C类空终止字符串。
答案 0 :(得分:11)
原因是您始终可以覆盖构造函数中的类内初始化列表。所以我想这最终会让人感到困惑。
struct Foo
{
Foo() {} // str = "test\0";
// Implementing this is easier if I can clearly see how big `str` is,
Foo() : str({'a','b', 'c', 'd'}) {} // str = "abcd0"
const char str[] = "test";
};
请注意,将const char
替换为static constexpr char
非常有效,而且可能是您想要的。
答案 1 :(得分:2)
如果允许编译器支持您描述的内容,并且str
的大小推断为5
,
Foo foo = {{"This is not a test"}};
会导致未定义的行为。
答案 2 :(得分:2)
正如评论中所提到的以及@sbabbi的回答,答案在于细节
12.6.2初始化基础和成员[class.base.init]
- 醇>
在非委托构造函数中,如果给定的非静态数据成员或 基类不是由mem-initializer-id指定的(包括 因为构造函数没有mem-initializer-list的情况 没有ctor-initializer)并且实体不是虚拟基类 抽象类(10.4),然后
- 如果实体是具有大括号或等于初始化程序的非静态数据成员,则实体初始化为 8.5;
- 否则,如果实体是匿名联合或变体成员(9.5),则不执行初始化;
- 否则,实体默认初始化
12.6.2初始化基础和成员[class.base.init]
- 醇>
如果给定的非静态数据成员同时具有 brace-or-equal-initializer和mem-initializer,初始化 由mem-initializer指定执行,以及非静态数据 member的brace-or-equal-initializer被忽略。 [例子:给定
struct A { int i = /∗ some integer expression with side effects ∗/ ; A(int arg) : i(arg) { } // ... };
A(int)构造函数只需将i初始化为arg的值, 并且i的括号或等于初始化器的副作用不会 地点。 - 结束例子]
因此,如果存在非删除构造函数,则忽略brace-or-equal-initializer,并且构造函数in-member初始化占优势。因此,对于省略了大小的阵列成员,表达式变得不正确。 §12.6.2,第9项,使得它更明确,我们指定如果构造函数执行mem初始化,则省略r值初始值设定项表达式。
此外,谷歌小组讨论Yet another inconsitent behavior in C++,进一步阐述并使其更清晰。它扩展了这个想法,解释了对于成员的成员内初始化不存在的情况,大括号初始化器是一种成员初始化的美化方式。作为一个例子
struct Foo {
int i[5] ={1,2,3,4,5};
int j;
Foo(): j(0) {};
}
相当于
struct Foo {
int i[5];
int j;
Foo(): j(0), i{1,2,3,4,5} {};
}
但是现在我们看到如果省略了数组大小,那么表达式将是错误的。
但后来说,编译器可能支持该功能,当成员未通过成员构造函数初始化初始化但目前为了统一,标准像许多其他东西一样,不支持此功能。