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

时间:2017-05-17 23:02:45

标签: 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);
}

这个问题是对Jason的Turner C ++每周第49/50期的后续跟进,他用std::forward<T>(t)...

定义了一个可变构造函数

1 个答案:

答案 0 :(得分:7)

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

  1. 构造函数的模板参数推导(P0091)。
  2. 扩展聚合初始化(P0017
  3. 现代化使用声明(P0195)。
  4. 这一行发生了什么:

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

    首先,我们使用模板参数推导(1)推断出c实际上是C<__lambda1, __lambda2>类型。这是通过使用演绎指南来完成的。

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

    最后,调用c(3)有效,因为(3)允许您简单地编写

    using T::operator()...;
    

    将lambdas的调用操作符引入C的范围,其中重载解析可以按预期工作。结果是我们调用__lambda2的调用操作符,它什么都不做。