如何编写聚合模板别名的推导指南?

Posted

技术标签:

【中文标题】如何编写聚合模板别名的推导指南?【英文标题】:How to write deduction guidelines for aliases of aggregate templates? 【发布时间】:2021-03-04 10:56:29 【问题描述】:

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

【问题讨论】:

从我读到的,别名的演绎指南使用源的演绎指南稍加调整,我看是不允许写别名的演绎指南。 【参考方案1】:

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

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

但是,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的返回类型的模板参数是由Adefining-type-id根据[temp.deduct.type]中的过程推导出来的,除了如果没有推导出所有模板参数,则推导不会失败。

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

具有单个部分特化,其模板参数列表是 A 的模板参数列表,其模板参数列表是 A 的特化模板参数列表 A ([temp.dep.type]),@ 987654365@ 匹配偏特化。

在我们的例子中,部分特化是:

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

所以deducible可以声明为:

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

由于N 可以从1 + sizeof...(U) 推导出来,array&lt;int, 1 + sizeof...(U)&gt; 始终是mytype&lt;N&gt;(又名std::arrray&lt;int, N&gt;)的有效匹配项,因此始终满足约束deducible&lt;array&lt;int, 1 + sizeof...(U)&gt;&gt;

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

相比之下,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

【讨论】:

以上是关于如何编写聚合模板别名的推导指南?的主要内容,如果未能解决你的问题,请参考以下文章

如何在查询中编写单个聚合

C++ 中聚合的带括号初始化的模板参数推导

如何使用 sqlalchemy 编写自己的聚合函数?

如何使用猫鼬羽毛适配器编写聚合?

采用 Eigen::Tensor 的函数 - 模板参数推导失败

如何以 CSV 格式编写窗口聚合?