【问题标题】:Template argument deduction for aggregate template with array带数组的聚合模板的模板参数推导
【发布时间】:2021-09-15 06:32:41
【问题描述】:

考虑以下程序:

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

【问题讨论】:

  • 与 gcc 10.3 相同的错误
  • 不完全是,gcc 10.3 甚至无法编译 A a{1,2};,而 msvc 可以

标签: c++ templates language-lawyer c++20


【解决方案1】:

GCC 是对的,代码应该被接受,并且a 的类型应该被推导出为A&lt;int&gt; 使用聚合的类模板参数推导(CTAD)。

N4868(最接近已发布的 C++20 标准)[over.match.class.deduct]/1 说(我已经排除了一些与此处无关的部分):

在解析推导类类型的占位符时,其中 template-name 命名一个主类模板C,形成了一组函数和函数模板,称为C 的向导 包括:
[...]
此外,如果定义了C,并且其定义满足 聚合类的条件,假设任何 依赖基类没有虚函数也没有虚基 类,并且初始值设定项是非空的 braced-init-list 或 带括号的expression-list,并且没有deduction-guide 对于C,该集合包含一个额外的函数模板,称为 综合扣除候选,定义如下。令 x1,...,xninitializer-listbraced-init-listexpression-list 的 /em> 或 designated-initializer-list。对于每个 xi,令 eiC 或其其中之一的对应聚合元素(可能是递归的)子聚合 这将由 xi if

初始化
  • 对于具有依赖的非数组类型或具有依赖值的数组类型的任何聚合元素,不考虑大括号省略 绑定,
    [...]

如果对于任何 xi 都不存在这样的聚合元素 ei,则 集合扣除候选项未添加到集合中。聚合体 扣减候选项如上从假设中推导出来的 构造函数C(T1,...,Tn),其中

  • [...]
  • 否则Tiei的声明类型,

[...]

A 的实例是聚合。只有 x11。关于不考虑大括号省略的要点不适用,因为我们有一个子聚合 (T t[2]),它是一个数组,它具有依赖类型但不依赖值绑定。因此考虑了大括号省略,1 将初始化t[0],即e1,并且是T 类型。

所以,总扣除候选是

template<typename T> A<T> F(T)

可以用来推导出A&lt;int&gt;

MSVC 无法生成此模板。错误信息显示它错误地生成了一个带有两个函数参数的函数。

P1816 中指定了聚合的 CTAD,但允许此操作的规则出现在另一篇论文中, P2082,解决了几个问题。 MSVC claims to implement both since VS 2019 16.7,但仍然拒绝 16.10.3 的代码。

作为附加数据点,EDG 6.2 严格 C++20 模式接受代码。

【讨论】:

    【解决方案2】:

    编辑:您的代码将不起作用,因为您试图通过访问尚未定义的嵌套成员来实例化您的模板。

    MSVC 无法为您推断类型,因此您需要显式实例化该类。

    template<typename T>
    struct A { T t[2] = {0, 0}; };
    
    int main()
    {
       A<int> a; //explicit instantiation 
       return a.t[1];
    }
    

    【讨论】:

    • 虽然这可能是正确的,但这并不是 OP 真正要求的。他们不想修复代码,他们想知道他们显示的代码是否根据语言规则有效。
    • 他们展示的代码不适用于 MSVC,因为它不支持聚合初始化。 OP 必须显式实例化模板才能使其与 MSVC 和 clang 一起使用。更新了我的答案。
    • 语言律师标记问题的目的不是解决代码问题,而是理解标准所说的内容。鼓励直接引用标准文本。解码语言的规则是目标;编译器做的是次要的。简单地解决代码中的问题是错误的方法。对于非语言律师问题,您的回答会很好。
    • 这个解释看起来不对,因为如果将A a{1};替换为A a{1,2};,那么MSVC编译程序不会出错。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-03
    • 2021-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-14
    • 1970-01-01
    相关资源
    最近更新 更多