Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variadic class template and inheritance - default compiler generated constructor

Tags:

c++

c++17

How come the below code works w/ just the default compiler generated constructor? I would expect it for POD but the struct below is probably not a POD so it must be something else.

template <typename ... T>
struct C : T ... {
   using T::operator()...;
};

// template class guidance feature of C++17
template <typename ... T>
C(T...) -> C<T...>;

int main(){
   C c { []{}, [](int){} };
   c(3);
}

This question comes as a follow up to Jason's Turner C++ weekly ep 49/50 where he defined a variadic constructor with std::forward<T>(t)...

like image 492
Kobi Avatar asked May 17 '17 23:05

Kobi


1 Answers

There are no constructors at play here. This code works due to a confluence of three features new in C++17:

  1. Template parameter deduction for constructors (P0091).
  2. Extending aggregate initialization (P0017)
  3. Modernizing using-declarations (P0195).

What happens in this line:

C c { []{}, [](int){} };

is that first, we use template parameter deduction (1) to deduce that c is really of type C<__lambda1, __lambda2>. This is done through the use of your deduction guide.

Next, since C<__lambda1, __lambda2> is an aggregate (due to (2)'s relaxation of base class restrictions - you are correct that is not considered an aggregate in C++11/14), we can use aggregate-initialization to initialize it. We do not use a constructor. The way aggregate initialization now works with base classes is that we just have to initializes the bases left-to-right. So the first expression ([]{}) is used to initialize the first base class (__lambda1) and the second expression ([](int){}) is used to initialize the second base class (__lambda2).

Lastly, the call c(3) works because (3) allowed you to simply write

using T::operator()...;

which brings in both lambdas' call operators into the scope of C, where overload resolution can work as expected. The result is that we call __lambda2's call operator, which does nothing.

like image 84
Barry Avatar answered Sep 24 '22 00:09

Barry