8
#include <array>

int main()
{
    struct A
    {
        unsigned char l;
        std::array<char, 12> c;
    };

    const A a = {1, "t"}; // OK
    const A& ar = {1, "t"}; // error: invalid initialization of reference of type 'const main()::A&' from expression of type '<brace-enclosed initializer list>'
}

(gcc 8.2, -std=c++17)

This question talks about a GCC bug, but it's old (7 years ago).

Note that I don't care about lifetime extension, I'm actually passing the temporary directly into a function for use rather than store it but I tried making the example clean.


Edit:

  • I cannot make the example smaller. In particular, it has to do with the array<char>.
  • Adding more braces around "t" and still fails.
  • Something that works is exploding the string literal into characters:

    const A& ar = {1, {'a', 'b'}}; // works
    
haelix
  • 4,245
  • 4
  • 34
  • 56
  • I think the code is correct and it's a gcc bug (although not related to the other question you linked) – M.M Jan 12 '19 at 00:29
  • Does indeed work on clang. https://wandbox.org/permlink/7oAmpGhjS4o7u7Oa – Baum mit Augen Jan 12 '19 at 00:32
  • as an aside, does adding an explicit nameless temporary yields the exact same result? Like so: `const A& art = A{1, "ab"};` This would sort it for what I'm doing – haelix Jan 12 '19 at 00:41
  • I'm not sure it's even valid to count on a simpler initialization like `std::array c{"hello"};`. Although a raw array member of type `T[N]` is the most likely implementation, the Standard only says that a `std::array` is an aggregate which can be list-initialized from an initializer list with up to `N` elements. So an implementation like `template struct array { T __elem; array __rest; };` would also be compliant. – aschepler Jan 12 '19 at 00:52
  • @aschepler in aggregate initialization it says that a string literal can be used to initialize a char array (and std::array is specified as being an aggregate containing an array) – M.M Jan 12 '19 at 02:36
  • @haelix that's what the code means anyway, so if the compiler accepts it then go for it – M.M Jan 12 '19 at 02:36
  • Oops, my recursive definition wouldn't support the requirement of being a contiguous container, or the `data()` member (unless maybe the implementation knows indexing the elements as array elements will work in practice). But @M.M I don't see any requirement that `std::array` contains an array element. – aschepler Jan 13 '19 at 00:19

1 Answers1

3

The first thing to notice is that the initializer {1, "t"} is using brace elision to initialize the sub-aggregate A.c which means that the literal "t" is taken to directly initialize the internal array that std::array holds. In this case, this array would look like char data[12].

This reduces to say that we are initializing a reference to const A with a brace-list containing an element that initializes a member array.

This will be somewhat equivalent to:

struct S {
    char const data[2];
};
S const& s = {"t"}; // fail for gcc

GCC has already a bug report on that.

You've already provided a workaround in the comment section. Just initialize the reference as:

const A& ar = A{1, "t"}
Jans
  • 11,064
  • 3
  • 37
  • 45