Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Consider the code:

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

int main()
{
    Foo foo;
}

It fails to compile with both g++ and clang++, spitting out essentially

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

I understand that this is what the standard probably says, but is there any particular good reason why? Since we have a string literal it seems that the compiler should be able to deduce the size without any problem, similarly to the case when you simply declare an out-of-class const C-like null terminated string.

like image 828
vsoftco Avatar asked Apr 12 '15 18:04

vsoftco


3 Answers

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.

like image 193
sbabbi Avatar answered Oct 16 '22 09:10

sbabbi


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.

like image 29
Abhijit Avatar answered Oct 16 '22 07:10

Abhijit


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.

like image 2
R Sahu Avatar answered Oct 16 '22 09:10

R Sahu