聚合模板别名推导指南怎么写?

How to write deduction guidelines for aliases of aggregate templates?

使用 C++20,可以为别名模板生成推导指南(请参阅 https://en.cppreference.com/w/cpp/language/class_template_argument_deduction 的“别名模板推导”部分)。然而,我无法让它们使用聚合初始化语法。在这种情况下,似乎没有生成别名的扣除指南。

看这个例子:

#include <array>

template <size_t N>
using mytype = std::array<int, N>;

// Deduction guideline ???

int main() {
    // mytype error_object = {1, 4, 7}; // ERROR
    mytype<3> object = {1, 4, 7}; // OK, but I have to manually specify the size.
    return object[0];
}

我曾尝试编写演绎指南,但每次都会遇到编译器错误。

template <typename T, typename ... U>
mytype(T, U...) -> mytype<1+sizeof...(U)>; // Compiler error

以及我能想到的任何其他指南。

是否可以自动推导数组别名的大小?

我正在使用 GCC 10.2

Is it even possible to have the size of the array alias automatically deduced?

我相信通过符合标准的实施应该是可能的。您不需要(也不能)添加任何更多指南。

但是,GCC 实现的 a different set of rules 比标准指定的要多:

This implementation differs from [the specification] in two significant ways:

1) We include all template parameters of A, not just some.
2) The added constraint is same_type instead of deducible.

实施者认为“这种简化对于实际使用应该具有相同的效果”。但显然情况并非如此:此实现在您的情况下无法正常工作 ICEs in some other cases.


作为参考,我将尝试遵循标准并展示如何生成 mytype 的指南。

我们有这个别名模板声明(别名模板在标准中称为A):

template <size_t N>
using mytype = std::array<int, N>;

以及来自标准库 ([array.cons]) 的推导指南:

template<class T, class... U>
array(T, U...) -> array<T, 1 + sizeof...(U)>;

首先根据推导指南([over.match.class.deduct]/1)生成函数模板(标准中称为f):

template<class T, class... U>
auto f(T, U...) -> array<T, 1 + sizeof...(U)>;

然后,根据 [over.match.class.deduct]/2:

the template arguments of the return type of f are deduced from the defining-type-id of A according to the process in [temp.deduct.type] with the exception that deduction does not fail if not all template arguments are deduced.

也就是说,我们从std::array<int, N>推导出array<T, 1 + sizeof...(U)>中的模板参数。在这个过程中,T被推导为intU 不可推导,所以保持原样。

推导结果代入函数模板,得到:

template<class T, class... U>
auto g(int, U...) -> array<int, 1 + sizeof...(U)>;

然后,我们生成一个函数模板f'f'g 具有相同的 return 类型和函数参数类型。 (如果 f 有特殊属性,它们被 f' 继承。)但值得注意的是, f' 的模板参数列表由 ([over.match.class.deduct]/(2.2),强调我的):

all the template parameters of A (including their default template arguments) that appear in the above deductions or (recursively) in their default template arguments, followed by the template parameters of f that were not deduced (including their default template arguments), otherwise f' is not a function template.

由于N没有出现在推导中,所以不包含在模板参数列表中(这是GCC与标准不同的地方)。

此外,f' 有一个约束条件 ([over.match.class.deduct]/(2.3)):

that is satisfied if and only if the arguments of A are deducible (see below) from the return type.

因此,按照标准,生成的函数模板是这样的:

template<class... U>
  requires deducible<array<int, 1 + sizeof...(U)>>
auto f'(int, U...) -> array<int, 1 + sizeof...(U)>;

显然,根据本指南可以推导出大小为1 + sizeof...(U)

下一步,我们看看deducible是如何定义的

[over.match.class.deduct]/3:

The arguments of a template A are said to be deducible from a type T if, given a class template

template <typename> class AA;

with a single partial specialization whose template parameter list is that of A and whose template argument list is a specialization of A with the template argument list of A ([temp.dep.type]), AA<T> matches the partial specialization.

在我们的例子中,部分专业化将是:

 template <size_t N> class AA<mytype<N>> {};

因此 deducible 可以声明为:

 template <class T> concept deducible = requires { sizeof(AA<T>); };

由于 N 可从 1 + sizeof...(U) 推导出来,array<int, 1 + sizeof...(U)> 始终是 mytype<N> 的有效匹配(a.k.a。std::arrray<int, N>),因此总是满足约束 deducible<array<int, 1 + sizeof...(U)>>

因此,根据标准,生成的指南是可行的,可以推导出大小。

相比之下,GCC 生成:

template<class... U, size_t N>
  requires same_type<array<int, 1 + sizeof...(U)>, mytype<N>>
auto f_(int, U...) -> array<int, 1 + sizeof...(U)>;

...无法推导出 N.