关于c ++:不能从成员变量的初始化字符串中推断出数组大小的原因是什么?

What is the reason for not being able to deduce array size from initializer-string in member variable?

考虑一下代码:

1
2
3
4
5
6
7
8
9
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的空终止字符串的情况类似。


原因是您总是有可能重写构造函数中的类内初始化程序列表。所以我想最后可能会很混乱。

1
2
3
4
5
6
7
8
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";
};

请注意,用static constexpr char替换const char可以很好地工作,而且可能无论如何都是您想要的。


如评论中所述和@sbabbi的回答,答案在于细节

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

  • In a non-delegating constructor, if a given non-static data member or
    base class is not designated by a mem-initializer-id (including the
    case where there is no mem-initializer-list because the constructor
    has no ctor-initializer) and the entity is not a virtual base class of
    an abstract class (10.4), then

    • if the entity is a non-static data member that has a brace-or-equal-initializer , the entity is initialized as specified in
      8.5;
    • otherwise, if the entity is an anonymous union or a variant member (9.5), no initialization is performed;
    • otherwise, the entity is default-initialized
  • 12.6.2初始化基础和成员[class.base.init]

  • If a given non-static data member has both a
    brace-or-equal-initializer and a mem-initializer, the initialization
    specified by the mem-initializer is performed, and the non-static data
    member’s brace-or-equal-initializer is ignored. [ Example: Given

    1
    2
    3
    4
    5
    struct A {
        int i = /? some integer expression with side effects ?/ ;
        A(int arg) : i(arg) { }
        // ...
    };
  • the A(int) constructor will simply initialize i to the value of arg,
    and the side effects in i’s brace-or equal-initializer will not take
    place. — end example ]

    因此,如果存在一个不删除的构造函数,则忽略大括号或相等的初始化程序,并且以构造函数成员内初始化为准。因此,对于省略大小的数组成员,该表达式将变得格式错误。 §12.6.2的第9项使我们更明确地指出,如果由构造函数执行mem初始化,则将省略r值初始化器表达式。

    此外,Google小组讨论了C ++中的另一种不一致的行为,这进一步说明了问题,并使它更加清晰。它扩展了解释"括号或相等初始化器"是成员不存在成员内初始化的情况下成员内初始化的一种美化方式的思想。举个例子

    1
    2
    3
    4
    5
    struct Foo {
        int i[5] ={1,2,3,4,5};
        int j;
        Foo(): j(0) {};
    }

    相当于

    1
    2
    3
    4
    5
    struct Foo {
        int i[5];
        int j;
        Foo(): j(0), i{1,2,3,4,5} {};
    }

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

    但是话又说回来,如果成员不是通过成员内构造函数初始化来初始化成员的,而当前出于一致性考虑,则该编译器可能已支持该功能,该标准与许多其他事物一样不支持此功能。


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

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

    会导致不确定的行为。