Lingxi
Lingxi

Reputation: 14987

Initializing aggregate base (GCC and clang disagree)

The following code compiles on clang 3.6 (C++14), but not on GCC 5.3 (C++14)

#include <array>
#include <utility>

struct super: std::array<int, 3> {
  using base = std::array<int, 3>;

  template <typename... Ts>
  super(Ts&&... xs)
      : base{std::forward<Ts>(xs)...} {
    // nop
  } 
};

int main() {
  super obj(1, 2, 3);
}

The error message produced is

/tmp/gcc-explorer-compiler116029-73-105rz4g/example.cpp: In instantiation of 'super::super(Ts&& ...) [with Ts = {int, int, int}]':
15 : required from here
9 : error: array must be initialized with a brace-enclosed initializer
: base{std::forward<Ts>(xs)...} {
^
9 : error: too many initializers for 'std::array<int, 3ul>'

I think I am initializing the base aggregate with a brace-enclosed initializer. No? What does the standard say about the syntax here?

Upvotes: 1

Views: 145

Answers (2)

Columbo
Columbo

Reputation: 60999

That's certainly allowed; see [array.overview], which guarantees that aggregate initialization for array works the way your example requires it to. You encountered a bug in GCC's brace elision, which didn't work properly if the aggregate is a base class:

struct A {int i[1];}; // Equivalent to libstdc++'s std::array<int, 1> implementation
                      // for this demonstration 
struct C : A {
  C() : A{0} {} 
};

This has been fixed in trunk: Demo with GCC trunk.

Upvotes: 5

SergeyA
SergeyA

Reputation: 62603

This is why I do not like so-called uniform initialization :). Use following code instead:

  super(Ts&&... xs)
      : base({std::forward<Ts>(xs)...})

Upvotes: 2

Related Questions