聚合模板别名如何写扣款指引?

使用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


解决方案

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

我认为,使用符合标准的实现应该是可能的。您不需要(也不能)添加更多辅助线。

但是,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:

f的返回类型的模板参数是根据[temp.duct.type]中的流程从A的定义类型-id中推导出来的,但如果没有推导出所有模板参数,则不会推导失败。

即,我们从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具有相同的返回类型和函数参数类型。(如果f有特殊属性,则由f'继承。)但值得注意的是,f'的模板参数列表由([over.match.class.deduct]/(2.2),重点是mine):

组成

A的所有模板参数(包括其默认模板参数)出现在上述演绎中或(递归)在其默认模板参数中,后跟未演绎的f的模板参数(包括其默认模板参数),否则f'不是函数模板。

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

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

这仅当且仅当A的参数可从返回类型中推断(见下文)时才满足。

因此,根据标准,生成的函数模板如下:

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:

模板A的参数被称为可以从类型T中推导出来,如果给定一个类模板

template <typename> class AA;

模板参数列表是A的模板参数列表是A的专门化,A模板参数列表是A([temp.des.type])的专门化,AA<T>匹配该部分专门化。

在我们的案例中,部分专业化认证为:

 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>(也称为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

相关文章