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() };
有什么办法可以同时进行模板类型推导和聚合初始化?
答案 0 :(得分:1)
首先,该术语是“类模板参数推导”。
第二,您需要的是扣除指南:
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>
答案 1 :(得分:1)
您对“聚合”一词有误解。
首先,您正在做的事情称为列表初始化。如果实例的类型是聚合,则列表初始化只会聚合初始化您的实例。聚合初始化允许您使用初始化程序列表按顺序初始化基类和/或类的成员。
来自cppreference:
聚集是以下类型之一:
- 数组类型
- 类类型(通常是struct或union),具有
- 不允许用户提供,继承或显式的构造函数(允许使用显式默认或删除的构造函数)
- 没有虚拟,私有或受保护的(自C ++ 17起)基类
- 没有虚拟成员函数
- 没有默认的成员初始化器
第二个要点适用于此处。由于在定义HAVE_MOVE_AND_COPY
时,您具有用户提供的构造函数(由用户写出的构造函数,而不是由编译器生成的构造函数),因此您的类型不是集合,并且编译器将仅寻找构造函数来初始化实例
Barry讨论了如何使用类模板参数推导进行聚合的其余部分。