同时具有"聚合初始化"和"模板推导"
Have both Aggregate Initialization and Template Deduction
背景:
C++17有两个伟大的特性:聚合初始化和模板类型推导(用于类(。聚合初始化允许您在不复制或移动字段的情况下实例化字段,模板类型推导使您不必指定参数的类型。
下面代码中的Wrapper
类就是一个例子。只要HAVE_MOVE_AND_COPY
未定义,它就有聚合初始化,但模板类型推导就不起作用。
另一方面,如果定义了HAVE_MOVE_AND_COPY
,则模板类型推导有效,但聚合初始化中断。我怎么能两者都有?
#include <cstdio>
#include <utility>
template<class T>
struct Wrapper {
T value;
#ifdef HAVE_MOVE_AND_COPY
Wrapper(T const & val) : value{val} {}
Wrapper(T && val) : value{std::move(val)} {}
#endif
Wrapper(Wrapper const &) = default;
Wrapper(Wrapper &&) = default;
};
struct VocalClass {
VocalClass() { puts("VocalClass()"); }
VocalClass(VocalClass const&) { puts("VocalClass(VocalClass const &)"); }
VocalClass(VocalClass &&) { puts("VocalClass(VocalClass &&)"); }
};
int main() {
Wrapper<VocalClass> w { VocalClass() };
#ifdef TRY_DEDUCTION
Wrapper w2 { VocalClass() };
#endif
}
示例:
不会发生移动或复制,但您没有模板推导:
$ c++ -std=c++17 example.cc && ./a.out
VocalClass()
有模板推导,但VocalClass
被移动:
$ c++ -DHAVE_MOVE_AND_COPY -DTRY_DEDUCTION -std=c++17 example.cc && ./a.out
VocalClass()
VocalClass(VocalClass &&)
VocalClass()
VocalClass(VocalClass &&)
如果没有HAVE_MOVE_AND_COPY
,模板类型的扣除将中断:
sky@sunrise:~$ c++ -DTRY_DEDUCTION -std=c++17 example.cc && ./a.out
example.cc: In function ‘int main()’:
example.cc:27:31: error: class template argument deduction failed:
Wrapper w2 { VocalClass() };
^
example.cc:27:31: error: no matching function for call to ‘Wrapper(VocalClass)’
example.cc:12:5: note: candidate: ‘template<class T> Wrapper(Wrapper<T>&&)-> Wrapper<T>’
Wrapper(Wrapper &&) = default;
^~~~~~~
example.cc:12:5: note: template argument deduction/substitution failed:
example.cc:27:31: note: ‘VocalClass’ is not derived from ‘Wrapper<T>’
Wrapper w2 { VocalClass() };
^
example.cc:11:5: note: candidate: ‘template<class T> Wrapper(const Wrapper<T>&)-> Wrapper<T>’
Wrapper(Wrapper const &) = default;
^~~~~~~
example.cc:11:5: note: template argument deduction/substitution failed:
example.cc:27:31: note: ‘VocalClass’ is not derived from ‘const Wrapper<T>’
Wrapper w2 { VocalClass() };
^
example.cc:5:8: note: candidate: ‘template<class T> Wrapper(Wrapper<T>)-> Wrapper<T>’
struct Wrapper {
^~~~~~~
example.cc:5:8: note: template argument deduction/substitution failed:
example.cc:27:31: note: ‘VocalClass’ is not derived from ‘Wrapper<T>’
Wrapper w2 { VocalClass() };
问题
有没有什么方法可以同时进行模板类型推导和聚合初始化?
首先,术语是"类模板参数推导">
其次,您需要的是推导指南:
template<class T>
struct Wrapper {
T value;
};
template <typename T>
Wrapper(T) -> Wrapper<T>; // this is a deduction guide
如果没有构造函数,您需要一些其他方法来指导推导。这就是它的目的,它允许:
Wrapper w{4}; // ok, Wrapper<int>
您对术语";合计";方法
首先,您正在进行的操作称为列表初始化。如果实例的类型为聚合,则列表初始化仅聚合初始化实例。聚合初始化允许您使用初始化器列表按顺序初始化基类和/或类的成员。
来自cppreference:
聚合是以下类型之一:
- 数组类型
- 类类型(通常为结构或联合(,具有
- 没有用户提供、继承或显式构造函数(允许显式默认或删除构造函数(
- 没有虚拟、私有或受保护的基类(自C++17以来(
- 没有虚拟成员函数
- 没有默认的成员初始值设定项
第二个要点适用于此处。由于在定义HAVE_MOVE_AND_COPY
时,您有一个用户提供的构造函数(由用户编写而不是由编译器生成的构造函数(,因此您的类型不是聚合,编译器将只查找用于初始化实例的构造函数。
Barry介绍了如何使用类模板参数推导进行聚合。
相关文章:
- 是否可以初始化不可复制类型的成员变量(或基类)
- C++使用整数的压缩数组初始化对象
- C++初始化基类
- 多成员Constexpr结构初始化
- 复制列表初始化的隐式转换的等级是多少
- 内联映射初始化的动态atexit析构函数崩溃
- 如何在C++中初始化嵌套类中的2个memeber
- 如何声明特征矩阵,然后通过嵌套循环初始化它
- 没有用于初始化C++中的变量模板的匹配构造函数
- 在未初始化映射的情况下,将值插入到映射的映射中
- C++成员初始化
- 为什么在C++中首先初始化成员类
- 同时具有"聚合初始化"和"模板推导"
- 初始化具有非默认构造函数的std::数组项的更好方法
- 是否可以在编译时初始化数组,以便在运行时不会花费时间?
- 我可以使用条件运算符初始化C风格的字符串文字吗
- 在C和C++中初始化结构中的数组
- 标准是否使用多余的大括号(例如 T{{{10}}})定义列表初始化?
- 在函数内部的声明中初始化数组,并在外部使用它
- 继承:构造函数,初始化C++11中基类的类C数组成员