Reputation: 878
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)...
Upvotes: 5
Views: 199
Reputation: 302643
There are no constructors at play here. This code works due to a confluence of three features new in C++17:
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.
Upvotes: 7