c++c++17uniform-initialization

Uniform vs legacy initialisation producing different compilation results


I alighted on this while permuting with a trivial piece of code:

struct Base0 {};
struct Base1 {};

template<typename... Ts>
struct Derived: Ts... {};

int main() {
    Derived<Base0, Base1> d0 {Base0{}, Base1{}}; // OK
    Derived<Base0, Base1> d1 (Base0{}, Base1{}); // ERROR
}

I thought both d0 and d1 should have resulted in a compilation error since I can't see how Derived without any matching ctor takes ctor arguments as passed and flags d0's compilation as fine.

There's probably something obvious I'm missing. What is it about the uniform initialisation that's making it pass ? Is it aggregate initialisation or something ? What's happening with the temporaries passed to the ctor ?

Using C++17 online compiler here

Edit

As asked, I'm providing a copy-paste of the spew-out:

main.cpp: In function ‘int main()’:
main.cpp:9:47: error: no matching function for call to ‘Derived::Derived(Base0, Base1)’
     Derived<Base0, Base1> d1 (Base0{}, Base1{}); // ERROR
                                               ^
main.cpp:5:8: note: candidate: constexpr Derived::Derived()
 struct Derived: Ts... {};
        ^~~~~~~
main.cpp:5:8: note:   candidate expects 0 arguments, 2 provided
main.cpp:5:8: note: candidate: constexpr Derived::Derived(const Derived&)
main.cpp:5:8: note:   candidate expects 1 argument, 2 provided
main.cpp:5:8: note: candidate: constexpr Derived::Derived(Derived&&)
main.cpp:5:8: note:   candidate expects 1 argument, 2 provided

Solution

  • Looks like this is a new C++17 feature of aggregate initialisation:

    Each direct public base, (since C++17) array element, or non-static class member, in order of array subscript/appearance in the class definition, is copy-initialized from the corresponding clause of the initializer list.

    It comes with the change that a class with bases may now be an aggregate (as long as they are not virtual, private, or protected… though they don't even need to be aggregates! 😲).

    Your failing case does not use aggregate initialisation, but instead attempts a good old-fashioned constructor invocation. As you've identified, no such constructor exists.