12

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

4

1 回答 1

10

甚至可以自动推导出数组别名的大小吗?

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

但是,GCC 实施的规则与标准规定的规则不同:

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.

实施者认为“这种简化对于实际使用应该具有相同的效果”。但显然情况并非如此:此实现在您的情况下无法正常工作,而ICE 在其他一些情况下则无法正常工作。


作为参考,我将尝试遵循标准并展示如何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

的返回类型的模板参数是根据[temp.deduct.type] 中的过程从定义类型 idf推导出来的,但如果不是所有模板参数都被推导出来,则推导不会失败。A

也就是说,我们从 中推导出模板array<T, 1 + sizeof...(U)>参数std::array<int, N>。在这个过程中,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),强调我的)组成:

出现在上述推导中或(递归地)在其默认模板实参中的所有模板形参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;

具有单个偏特化,其模板形参列表是 ofA并且其模板实参列表是A具有A([temp.dep.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),因此始终是(aka )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

于 2020-11-21T08:38:04.593 回答