Variadic类模板和继承 - 默认编译器生成的构造函数

Kob*_*obi 5 c++ c++17

为什么下面的代码只能使用默认的编译器生成的构造函数?我希望它适用于POD,但下面的结构可能不是POD所以它必须是别的东西.

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);
}
Run Code Online (Sandbox Code Playgroud)

这个问题是对Jason的Turner C++每周第49/50期的后续跟进,他定义了一个可变构造函数 std::forward<T>(t)...

Bar*_*rry 7

这里没有施工人员在玩.此代码的工作原理是由于C++ 17中新增的三个特性的汇合:

  1. 构造函数的模板参数推导(P0091).
  2. 扩展聚合初始化(P0017)
  3. 现代化使用声明(P0195).

这一行会发生什么:

C c { []{}, [](int){} };
Run Code Online (Sandbox Code Playgroud)

首先,我们使用模板参数推导(1)来推断出c真正的类型C<__lambda1, __lambda2>.这是通过使用扣除指南完成的.

接下来,由于C<__lambda1, __lambda2>是聚合(由于(2)放宽了基类限制 - 你是正确的,在C++ 11/14中不被认为是聚合),我们可以使用聚合初始化来初始化它.我们使用构造函数.聚合初始化现在与基类一起使用的方式是我们只需要从左到右初始化基数.所以第一个表达式([]{})用于初始化第一个基类(__lambda1),第二个表达式([](int){})用于初始化第二个基类(__lambda2).

最后,这个电话c(3)有效,因为(3)允许你简单地写

using T::operator()...;
Run Code Online (Sandbox Code Playgroud)

这将lambdas的调用操作符引入到范围内C,其中重载决策可以按预期工作.结果是我们调用了__lambda2调用操作符,它什么都不做.