Why can I initialize a regular array from {}, but not a std::array
Asked Answered
S

3

11

This works:

int arr[10] = {};

All elements of arr are value-initialized to zero.

Why doesn't this work:

std::array<int, 10> arr({}); 

I get the following warning from g++ (version 4.8.2):

warning: missing initializer for member ‘std::array<int, 10ul>::_M_elems’

Sounder answered 7/7, 2015 at 14:48 Comment(5)
"this doesn't work ... I get the following warning" So it worked then. If it didn't work it wouldn't have compiled!Samuelson
@JonathanWakely I believe the OP was attempting to express surprise by the warning and I found it surprising as well. This is indeed problematic is you are using -Werror which I do. It is good to see the warning was removed it recent versions but that does not help those who can't upgrade :-(Marquesan
@ShafikYaghmour, but the OP wasn't using -Werror (or it wouldn't say "warning") and indiscriminate use of -Werror without judicious use of -Wno-xxxx is not necessarily a good idea. I understand that the warning is surprising, but it does work, all the elements are initialized to zero as expected. (Although as AnT points out, using ({}) is weird and should be discouraged, children will point and laugh at you in the street if you do that).Samuelson
@JonathanWakely yes agreed, using -Wno-xxxx is useful but in this case I may not want to use -Wno-missing-field-initializers since I may want the other cases it warns about. Agreed that ({}) is indeed weird but if the example was changed to std::array<int, 10> arr = {}; the question would still stand.Marquesan
... and arguably an answer the solely points out that ({}) is weird would not really be an answer. Probably be considered a bad answer since it now blocks a reasonable edit to the question which would simplify it and remove what is not really the core issue.Marquesan
M
12

There are two issues one which is a matter of style and the warning.

Although it may not be obvious, aggregate initialization is happening on a temporary which is then being used as an argument to the copy constructor. The more idiomatic to do this initialization would be as follows:

std::array<int, 10> arr = {}; 

Although this still leaves the warning.

The warning is covered by gcc bug report: - -Wmissing-field-initializers relaxation request and one of the comments says:

[...]Surely, the C++ syntax of saying MyType x = {}; should be supported, as seen here:

http://en.cppreference.com/w/cpp/language/aggregate_initialization

where for instance:

struct S {
  int a;
  float b;
  std::string str;
};

S s = {}; // identical to S s = {0, 0.0, std::string};

That shouldn't warn for the reasons stated in earlier comments.

and a follow-up comment says:

My statement about zero-initialization was inaccurate (thanks), but the general point still stands: in C you have to write ' = {0}' since empty-braces initializer is not supported by the language (you get a warning with -pedantic); in C++, you can write ' = {}' or 'T foo = T();', but you don't need to write ' = {0}' specifically.

The latest versions of gcc does not produce this warning for this case, see it live working with gcc 5.1.

We can see this topic also covered in the Clang Developers lists in the thead: -Wmissing-field-initializers.

For reference the draft C++11 standard section 8.5.1 [dcl.init.aggr] says:

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). [ Example:

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. —end example ]

So as this is valid C++, although as noted using {} is not valid C99. One could argue that it is only a warning, but this seems like idiomatic C++ using {} for aggregate initialization and is problematic if we are using -Werror to turn warnings into errors.

Marquesan answered 7/7, 2015 at 14:51 Comment(2)
I just ran into this issue in my own code was doing the research a little while ago.Marquesan
Judging from your comments on this question, this issue is causing you some distress. My answer below provides an easy solution.Herriot
H
5

Firstly, you can use the ({}) initializer with an std::array object, but semantically that stands for direct-initialization using copy-constructor from a temporary value-initialized std::array object, i.e. it is equivalent to

std::array<int, 10> arr(std::array<int, 10>{}); 

And it is actually supposed to compile.

Secondly, you don't really have to go the ({}) way when you can just do

std::array<int, 10> arr = {};

or

std::array<int, 10> arr{};

The first of the two is the most syntactically similar to your int arr[10] = {};, which makes me wonder why you didn't try it at first. Why did you decide to use ({}) instead of = {} when you built the std::array version of = {} syntax?

Heiress answered 7/7, 2015 at 19:13 Comment(4)
Note, it does compile, it is just a warning, which is an issue if you use -Werror. Neither of the suggested alternatives removes the warning, see it live. At the end of the day it still comes down to aggregate initialization and an unnecessary warning for what should be considered an idiomatic way to initialize an aggregate.Marquesan
Because I thought (mistakenly) that this would call the default constructor. See this post: #31278877Sounder
@ShafikYaghmour the C++ standard doesn't distinguish between "warning" and "error". Common compilers are inconsistent in this respect too; for example "warning" is given by gcc for some constraint violations as well as for some correct code. So it is not correct for someone to dismiss a message because it is "just a warning"; the problem must be understood first.Gambell
@MattMcNabb Agreed but not sure how that pertains to my comment. They are not violating the standard here and I did not say they were but it is a surprising warning and it the warning was removed once the bug report was filed.Marquesan
H
4

Enough people have pointed this out as a "problem" when compiling with -Werror that I think it's worth mentioning that the problem goes away if you just double up:

std::array<int, 10> arr{{}};

Does not produce any warning for me on gcc 4.9.2.

To add a bit as to why this solves it: my understanding was that std::array is actually a class with a C array as its only member. So double up on the braces makes sense: the outer braces indicate you are initializing the class, and then the inner braces default initialize the one and only member of the class.

Since there is no possible ambiguity when there is only one variable in a class, just using one pair of {} should be reasonable, but gcc is overly pedantic here, and warns.

Herriot answered 9/7, 2015 at 2:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.