3

I've found that when a simple data struct with default constructors contains an array, the default constructor can be called with a different number of arguments, ie:

struct LayerData
{
    uint32_t d[60];
};

Can be initialized by:

LayerData in({rand(),rand(),rand(),rand(),rand()});

And it compiles properly.

Is this the expected behaviour in C++11? Is there no compile-time checking of sizes in the implicit constructor?

3
  • gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.1) Commented May 8, 2016 at 10:51
  • 8.5.1/4: "An aggregate that is a class can also be initialized with a single expression not enclosed in braces". It's not that the length is not checked; rather, your initialization is like int a[3] = {1};. Commented May 8, 2016 at 11:08
  • Ok, I just was expecting int a[3] = {1}; to also fail. But in the answer below, it is quoted form the standard that it is not. Commented May 8, 2016 at 11:17

3 Answers 3

3

N3337 8.5.1/7

If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member not explicitly initialized shall be initialized from an empty initializer list (8.5.4).

struct S { int a; const char* b; int c; };
S ss = { 1, "asdf" };

initializes ss.a with 1, ss.b with "asdf", and ss.c with the value of an expression of the form int(), that is, 0.

So in your example first 5 elements are initialized with rand() other with int() which is 0.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for the reference
That doesn't seem to explain why it works with parenthesizes instead of curly braces. T var(...) should always invoke a constructor. And T in this case is an aggregate which has no constructor. So this shouldn't work at all.
@NicolBolas I thought that OP was not asking about that. Anyway, that's interesting question.
2

There is compile-time checking. This will not compile:

struct A
{
  int b[3];
};

int main()
{
  A a { 1, 2 };       // no problem here. equivalent to 1, 2, 0
  A b { 1, 2, 3, 4 }; // problem here. too many initializers
}

Because:

/tmp/gcc-explorer-compiler11648-73-eh15v1/example.cpp: In function 'int main()':
10 : error: too many initializers for 'A'
A b { 1, 2, 3, 4 };
^
Compilation failed

An array may be initialised with fewer items than it contains. In this case the remaining elements are value initialised (i.e. zero).

Comments

0

GCC 4.8.4 will compile the code fine. Not every compiler does, MSVC++ 14 (VS 2015) doesn't compile

LayerData in({rand(),rand(),rand(),rand(),rand()});

But it does compile

LayerData in{{rand(),rand(),rand(),rand(),rand()}};

Using C++ 11 Universal notation

Looking at the reason GCC permits this, it seems to be because it allocates the type on the stack and then uses XMM to move 32 bits (64 when compiled against x64) at a time into the memory location. Maybe this changes with the size of the type, but generally I'd say if you need to enforce the same length you shouldn't be exposing the type like this anyways. Do note that universal notation can actually be used to your advantage and with templates you can form something that looks a lot similar to what you're trying to do and enforces the same amount of arguments:

#include <cstdint>

template <int N>
class LayerData
{
    static_assert(N > 0, "Number must be greater than 0");
public:
    uint32_t d[N];
    template<typename... Args>
    LayerData(Args&&... args) : d{uint32_t(args)...}
    {
        static_assert(sizeof...(Args) == N, "Invalid number of constructor arguments.");
    }
};

int main()
{
    LayerData<4> in1{1, 2, 3, 4}; //Fine
    LayerData<60> in2{1, 2, 3, 4}; //Causes error "Invalid number of constructor arguments."
}

Hastily written, but you should get the idea.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.