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

前端 未结 3 756
时光取名叫无心
时光取名叫无心 2020-12-03 17:19

Consider the code:

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

int main()
{
    Foo foo;
}

It fails to compile with both g++ and clan

相关标签:
3条回答
  • 2020-12-03 17:40

    If the compiler was allowed to support what you described, and the size of str was deduced to 5,

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

    will lead to undefined behavior.

    0 讨论(0)
  • 2020-12-03 17:51

    As mentioned in the comments and as answered by @sbabbi, the answer lies in the details

    12.6.2 Initializing bases and members [class.base.init]

    1. 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 Initializing bases and members [class.base.init]

    1. 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

      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 ]

    So, if there is a non-deleting constructor, the brace-or-equal-initializer is ignored, and the constructor in-member initialization prevails. Thus, for array members for which the size is omitted, the expression becomes ill-formed. §12.6.2, item 9, makes it more explicit where we it specified that the r-value initializer expression is omitted if mem-initialization is performed by the constructor.

    Also, the google group dicussion Yet another inconsitent behavior in C++, further elaborates and makes it more lucid. It extends the idea in explaining that brace-or-equal-initializer is a glorified way of an in-member initialization for cases where the in-member initialization for the member does not exist. As an example

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

    is equivalent to

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

    but now we see that if the array size was omitted, the expression would be ill-formed.

    But then saying that, the compiler could have supported the feature for cases when the member is not initialized by in-member constructor initialization but currently for the sake of uniformity, the standard like many other things, does not support this feature.

    0 讨论(0)
  • 2020-12-03 17:56

    The reason is that you always have the possibility to override an in-class initializer list in the constructor. So I guess that in the end, it could be very confusing.

    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";
    };
    

    Notice that replacing const char with static constexpr char works perfectly, and probably it is what you want anyway.

    0 讨论(0)
提交回复
热议问题