以下代码很短,但会导致不同的编译器行为不一致:
#include <tuple>
template <typename T = int, typename... Ts>
using tpl = std::tuple<T, Ts...>;
tpl x; // I would assume this should be std::tuple<int>
Clang和MSVC表示,模板参数推导不适用于别名模板,ICC表示缺少模板参数,而GCC有一个internal compiler error
。我通常会认为这表明别名模板没有经过推导,尤其是根据cpprreference,它们没有经过推导(我知道这不是官方资源,只是引用(,但我想确定。
这段代码真的格式不正确吗?还是这些编译器中还没有实现别名模板默认模板参数?我以为它们是在C++17或C++20中添加的。
C++20为别名模板引入了CTAD
别名模板可能确实有默认的模板参数,根据[temp.param]/14:,将默认模板参数设置为后面跟着模板参数包的模板参数是合法的
如果类模板、变量模板或别名模板的模板参数具有默认模板参数,则每个后续模板参数都应提供默认模板参数或是模板参数包。[…]
然而,默认模板参数是转移注意力,这里的关键是类模板参数推导是否对别名模板有效,我们可以将您的示例最小化为以下示例:
#include <tuple>
template <typename T>
using tpl = std::tuple<T>;
tpl x{1}; // should deduce tpl<int> in C++20
// Clang: error
// GCC 9.3: error
// GCC 10.1: ICE / internal compiler error
根据C++20接受的P1814R0(1(,上面的最小示例确实是合法的,但Clang尚未实现P1814R0,这解释了Clang拒绝它的原因。另一方面,GCC列出了为GCC 10实现的P1814RO,这意味着它应该为C++20接受它。
(1(根据C++20和P1814R0(别名模板的类模板参数推导的措辞(,(原始提案P1021R4的措辞(CTAD也适用于别名模板,但不允许对其进行明确的推导指南
在C++17中,当使用别名模板时,您需要包括模板参数列表(即使它是空的(-在C++17:中,没有等效于类模板参数推导的别名模板
#include <tuple>
template <typename T = int, typename... Ts>
using tpl = std::tuple<T, Ts...>;
tpl<> x; // OK in GCC and Clang
ICE(内部编译器错误(始终是一个错误,无论代码是格式错误还是格式良好,如上所述,GCC只在10.1及更高版本中发出ICE,而在以前的版本中会产生错误。
因此,GCC显然有10.1的ICE回归(当实现别名模板的CTAD时,它被可疑地列为目标(。它至少与以下错误报告有关:
- Bug 96199-[10/11 Regression]内部编译器错误:在别名模板的带有CTAD的tsubst_copy中
然而,它被列为已解决,而您的示例仍然为GCC中继生成一个ICE,其中包括对96199的修复。
我们最后可能会注意到,GCC成功地将CTAD应用于别名模板,其中我们只使用模板参数包:
#include <tuple>
template <typename... Ts>
using tpl = std::tuple<Ts...>;
tpl x{1}; // OK
但如果我们在最小的例子中用CCD_ 3代替CCD_
#include <vector>
template <typename T>
using vec = std::vector<T>;
vec x{{1}}; // GCC 10.1: ICE
我们为GCC 10.1获得了另一种ICE(以及转发(,而添加默认模板参数并用默认初始化替换支持的直接初始化是可以接受的。
#include <vector>
template <typename T = int>
using vec = std::vector<T>;
vec x; // GCC 10.1: OK