1

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?

Lingxi
  • 14,579
  • 2
  • 37
  • 93

2 Answers2

5

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.

Columbo
  • 60,038
  • 8
  • 155
  • 203
  • My [library](https://github.com/Lingxi-Li/Happy_Hacking_CXX) compiles fine on my Mac OS X with clang. When my buddy tries to compile it on GCC, everything just breaks :-( – Lingxi Jan 29 '16 at 15:42
  • @dyp So the crux is that [the aggregate was the base class](http://melpon.org/wandbox/permlink/yQWHjrv7BjduVD1b)... – Columbo Jan 30 '16 at 12:12
  • 1
    Meh, [array.overview] isn't really on point. You aren't using = in the mem-initializer, after all. – T.C. Jan 30 '16 at 12:30
  • @T.C. `array` is an aggregate. If copy-list-initialization works, so must direct-list-initialization. (Unless there's compiler magic involved to make precisely the former work, which would be idiotic.) The fact that brace elision is exploited is just an implementation detail we shouldn't need to care about. – Columbo Jan 30 '16 at 12:36
  • Actually, this feels underspecified. Perhaps the original wording was written with the pre-CWG1270 rule in mind and hasn't been updated? – T.C. Jan 30 '16 at 17:13
  • @T.C. This is not underspecified AFAICS: Aggregate-initialization must kick in, and if it is required to work with `={}` it must work with `{}` as well. Anyway, I'll submit an LWG issue. – Columbo Jan 30 '16 at 17:20
2

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

  super(Ts&&... xs)
      : base({std::forward<Ts>(xs)...})
SergeyA
  • 61,605
  • 5
  • 78
  • 137