21

考虑代码:

struct Foo
{
    const char str[] = "test";
};

int main()
{
    Foo foo;
}

它无法同时使用 g++ 和 clang++ 编译,本质上是吐出

error: array bound cannot be deduced from an in-class initializer

我知道这可能是标准所说的,但是有什么特别好的理由吗?由于我们有一个字符串文字,因此编译器似乎应该能够毫无问题地推断出大小,类似于您简单地声明一个类外constC 类空终止字符串的情况。

4

3 回答 3

17

原因是您始终可以在构造函数中覆盖类内初始化程序列表。所以我想最后,它可能会非常混乱。

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 charwithstatic constexpr char可以完美地工作,并且可能无论如何都是您想要的。

于 2015-04-12T18:55:59.290 回答
5

正如评论中提到的和@sbabbi 所回答的那样,答案在于细节

12.6.2 初始化基和成员 [class.base.init]

  1. 在非委托构造函数中,如果给定的非静态数据成员或基类不是由 mem-initializer-id 指定的(包括由于构造函数没有 ctor-initializer 而没有 mem-initializer-list 的情况)并且实体不是抽象类(10.4)的虚拟基类,则

    • 如果实体是具有大括号或相等初始化器的非静态数据成员,则实体按照 8.5 中的规定进行初始化;
    • 否则,如果实体是匿名联合或变体成员(9.5),则不执行初始化;
    • 否则,实体默认初始化

12.6.2 初始化基和成员 [class.base.init]

  1. 如果给定的非静态数据成员同时具有大括号或等式初始化器和内存初始化器,则执行内存初始化器指定的初始化,并且非静态数据成员的大括号或等式初始化器是忽略。[示例:给定

    struct A { 
        int i = /∗ some integer expression with side effects ∗/ ; 
        A(int arg) : i(arg) { } 
        // ... 
    };
    

A(int) 构造函数将简单地将 i 初始化为 arg 的值,并且 i 的大括号或相等初始化器中的副作用不会发生。—结束示例]

所以,如果有非删除构造函数,则忽略大括号或等号初始化器,以构造函数的成员内初始化为准。因此,对于省略了大小的数组成员,表达式变得不正确。§12.6.2,第 9 项,在我们指定如果 mem-initialization 由构造函数执行时省略 r-value 初始化表达式的地方更加明确。

此外,谷歌小组讨论了 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} {};
}

但是现在我们看到,如果省略了数组大小,则表达式将是格式错误的。

但是话说回来,当成员没有被成员内构造函数初始化但目前为了统一起见,标准像许多其他东西一样,不支持此功能的情况下,编译器本可以支持该功能。

于 2015-04-12T19:27:41.633 回答
2

如果允许编译器支持您所描述的内容,并且将 的大小str推断为5

Foo foo = {{"This is not a test"}};

将导致未定义的行为。

于 2015-04-12T18:56:44.713 回答