带数组的聚合模板的模板参数推导
Template argument deduction for aggregate template with array
考虑以下程序:
template<typename T>
struct A { T t[2]; };
int main()
{
A a{1}; // only one value provided for 2-element array
return a.t[0];
}
在 C++20 模式下,它在 gcc
中编译良好,但在 Visual Studio 中失败并出现错误:
<source>(6): error C2641: cannot deduce template arguments for 'A'
<source>(6): error C2780: 'A<T> A(void)': expects 0 arguments - 1 provided
<source>(2): note: see declaration of 'A'
<source>(6): error C2784: 'A<T> A(A<T>)': could not deduce template argument for 'A<T>' from 'int'
<source>(2): note: see declaration of 'A'
<source>(6): error C2784: 'A<T> A(A<T>)': could not deduce template argument for 'A<T>' from 'int'
<source>(2): note: see declaration of 'A'
<source>(6): error C2780: 'A<T> A(T,T)': expects 2 arguments - 1 provided
<source>(2): note: see declaration of 'A'
https://gcc.godbolt.org/z/6ejfW8G77
这里是两个编译器中的哪一个?
(Clang
在这里没有问题,因为它还不支持聚合的括号初始化)。
更新:微软承认了这个错误并承诺很快修复:
https://developercommunity.visualstudio.com/t/template-argument-deduction-fails-in-case-of-aggre/1467260
编辑:您的代码将无法运行,因为您试图通过访问尚未定义的嵌套成员来实例化您的模板。
MSVC 将无法为您推断类型,因此您需要显式实例化 class。
template<typename T>
struct A { T t[2] = {0, 0}; };
int main()
{
A<int> a; //explicit instantiation
return a.t[1];
}
GCC 是正确的,代码应该被接受,a
的类型应该使用 class 聚合模板参数推导 (CTAD) 推导为 A<int>
。
N4868(最接近已发布的 C++20 标准)[over.match.class.deduct]/1 说(我排除了一些与此处无关的部分):
When resolving a placeholder for a deduced class type where the
template-name names a primary class template C
, a set of functions and function templates, called the guides of C
, is formed
comprising:
[...]
In addition, if C
is defined and its definition satisfies the
conditions for an aggregate class with the assumption that any
dependent base class has no virtual functions and no virtual base
classes, and the initializer is a non-empty braced-init-list or
parenthesized expression-list, and there are no deduction-guides
for C
, the set contains an additional function template, called the
aggregate deduction candidate, defined as follows. Let x1,...,xn be the elements of the initializer-list or designated-initializer-list of the braced-init-list, or of the expression-list. For each xi, let ei be the corresponding aggregate element of C
or of one of its (possibly recursive) subaggregates
that would be initialized by xi if
- brace elision is not considered for any aggregate element that has a dependent non-array type or an array type with a value-dependent
bound,
[...]
If there is no such aggregate element ei for any xi, the
aggregate deduction candidate is not added to the set. The aggregate
deduction candidate is derived as above from a hypothetical
constructor C(T1,...,Tn)
, where
- [...]
- otherwise,
Ti
is the declared type of ei,
[...]
A
的实例是聚合。只有x1,1
。关于不考虑大括号省略的项目符号不适用,因为我们有一个子聚合 (T t[2]
),它是一个具有依赖类型但不依赖值的边界的数组。因此考虑大括号省略,1
将初始化 t[0]
,即 e1,类型为T
.
所以,合计扣除候选为
template<typename T> A<T> F(T)
可用于推导A<int>
。
MSVC 无法生成此模板。错误消息显示它错误地生成了一个带有两个函数参数的函数。
聚合的 CTAD 在 P1816 中指定,但允许此工作的规则出现在另一篇论文中,
P2082, that fixed several problems. MSVC claims to implement both since VS 2019 16.7,但仍然拒绝 16.10.3 的代码。
作为附加数据点,EDG 6.2 在严格的 C++20 模式下接受代码。
考虑以下程序:
template<typename T>
struct A { T t[2]; };
int main()
{
A a{1}; // only one value provided for 2-element array
return a.t[0];
}
在 C++20 模式下,它在 gcc
中编译良好,但在 Visual Studio 中失败并出现错误:
<source>(6): error C2641: cannot deduce template arguments for 'A'
<source>(6): error C2780: 'A<T> A(void)': expects 0 arguments - 1 provided
<source>(2): note: see declaration of 'A'
<source>(6): error C2784: 'A<T> A(A<T>)': could not deduce template argument for 'A<T>' from 'int'
<source>(2): note: see declaration of 'A'
<source>(6): error C2784: 'A<T> A(A<T>)': could not deduce template argument for 'A<T>' from 'int'
<source>(2): note: see declaration of 'A'
<source>(6): error C2780: 'A<T> A(T,T)': expects 2 arguments - 1 provided
<source>(2): note: see declaration of 'A'
https://gcc.godbolt.org/z/6ejfW8G77
这里是两个编译器中的哪一个?
(Clang
在这里没有问题,因为它还不支持聚合的括号初始化)。
更新:微软承认了这个错误并承诺很快修复: https://developercommunity.visualstudio.com/t/template-argument-deduction-fails-in-case-of-aggre/1467260
编辑:您的代码将无法运行,因为您试图通过访问尚未定义的嵌套成员来实例化您的模板。
MSVC 将无法为您推断类型,因此您需要显式实例化 class。
template<typename T>
struct A { T t[2] = {0, 0}; };
int main()
{
A<int> a; //explicit instantiation
return a.t[1];
}
GCC 是正确的,代码应该被接受,a
的类型应该使用 class 聚合模板参数推导 (CTAD) 推导为 A<int>
。
N4868(最接近已发布的 C++20 标准)[over.match.class.deduct]/1 说(我排除了一些与此处无关的部分):
When resolving a placeholder for a deduced class type where the template-name names a primary class template
C
, a set of functions and function templates, called the guides ofC
, is formed comprising:
[...]
In addition, ifC
is defined and its definition satisfies the conditions for an aggregate class with the assumption that any dependent base class has no virtual functions and no virtual base classes, and the initializer is a non-empty braced-init-list or parenthesized expression-list, and there are no deduction-guides forC
, the set contains an additional function template, called the aggregate deduction candidate, defined as follows. Let x1,...,xn be the elements of the initializer-list or designated-initializer-list of the braced-init-list, or of the expression-list. For each xi, let ei be the corresponding aggregate element ofC
or of one of its (possibly recursive) subaggregates that would be initialized by xi if
- brace elision is not considered for any aggregate element that has a dependent non-array type or an array type with a value-dependent bound,
[...]If there is no such aggregate element ei for any xi, the aggregate deduction candidate is not added to the set. The aggregate deduction candidate is derived as above from a hypothetical constructor
C(T1,...,Tn)
, where
- [...]
- otherwise,
Ti
is the declared type of ei,[...]
A
的实例是聚合。只有x1,1
。关于不考虑大括号省略的项目符号不适用,因为我们有一个子聚合 (T t[2]
),它是一个具有依赖类型但不依赖值的边界的数组。因此考虑大括号省略,1
将初始化 t[0]
,即 e1,类型为T
.
所以,合计扣除候选为
template<typename T> A<T> F(T)
可用于推导A<int>
。
MSVC 无法生成此模板。错误消息显示它错误地生成了一个带有两个函数参数的函数。
聚合的 CTAD 在 P1816 中指定,但允许此工作的规则出现在另一篇论文中, P2082, that fixed several problems. MSVC claims to implement both since VS 2019 16.7,但仍然拒绝 16.10.3 的代码。
作为附加数据点,EDG 6.2 在严格的 C++20 模式下接受代码。