别名模板可以有默认模板参数吗?

Can alias templates have default template parameters?

以下代码很短,但会导致不同编译器的行为disagree:

#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。我通常会认为别名模板不会被扣除的迹象 - 特别是根据 cppreference 他们不会(我知道这不是官方资源,只是 reference) - 但我想确定一下。

这段代码真的格式错误还是别名模板默认模板参数只是尚未在这些编译器中实现?我以为它们是在 C++17 或 C++20 中添加的。

C++20 为别名模板引入了 CTAD

别名模板可能确实有默认模板参数,根据 [temp.param]/14:

,模板参数的默认模板参数后跟模板参数包是合法的

If a template-parameter of a class template, variable template, or alias template has a default template-argument, each subsequent template-parameter shall either have a default template-argument supplied or be a template parameter pack. [...]

然而,默认模板参数是一个红色鲱鱼,这里的关键是 class 模板参数推导对于别名模板是否有效,我们可以将您的示例最小化为以下示例:

#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

根据P1814R0(1), which was accepted for C++20, the minimal example above is indeed legal, but Clang is yet to implement P1814R0, explaining why Clang rejects it. GCC, on the other hand, lists P1814R0 as implemented for GCC 10,这意味着它应该接受 C++20。

(1) 根据 C++20 和 P1814R0 (Wording for Class Template Argument Deduction for Alias Templates), (/wording for original proposal P1021R4) CTAD 也适用于别名模板,但不允许对它们进行显式推导。

在 C++17 中,您需要在使用别名模板时包括模板参数列表(即使它是空的)——在 C++17 中,没有等效于 class 别名模板的模板参数推导:

#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 时,它被可疑地列为目标)。它至少与以下错误报告有关:

然而,这被列为已解决,而您的示例仍然为 GCC 主干生成一个 ICE,其中包括对 96199 的修复。


我们可能最终会注意到 GCC 成功地将 CTAD 应用于我们仅使用模板参数包的别名模板:

#include <tuple>

template <typename... Ts>
using tpl = std::tuple<Ts...>;

tpl x{1}; // OK

但是如果我们在最小示例中将 std::tuple 替换为 std::vector

#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