【问题标题】:Why is the braced-init-list not supported in an aggregate deduction but brace elision is supported?为什么聚合推导不支持大括号初始化列表但支持大括号省略?
【发布时间】:2023-03-11 07:23:01
【问题描述】:

为什么聚合推导不支持大括号初始化列表,但支持大括号省略?

#include <iostream>
template<typename T>
struct Test{
    T t[2];
};
int main(){
  Test t{{1,2}};  // #1
  // Test t1{1,2} // #2
}

#1 是 GCC 的 rejected,而 #2 将被 GCC 接受。

根据over.match.class.deduct#1

此外,如果定义了 C 并且它的定义满足聚合类 ([dcl.init.aggr]) 的条件,并假设任何依赖的基类都没有虚函数和虚基类,并且初始化器是一个非空的大括号初始化列表或带括号的表达式列表,并且没有 C 的推导指南,该集合包含一个附加的函数模板,称为聚合推导候选,定义如下。令 X1,...,XN 是括号初始化列表或表达式的初始化列表或指定初始化列表的元素-列表。对于每个 Xi,设 ei 是 C 的相应聚合元素,或者是由 Xi 如果

  • [1.5] 大括号省略不考虑用于具有依赖的非数组类型或具有依赖于值的边界的数组类型的任何聚合元素,并且
  • [1.6] 假设作为包扩展的每个非尾随聚合元素不对应于初始化列表中的任何元素,并且
  • [1.7] 假设作为包扩展的尾随聚合元素对应于初始化列表的所有剩余元素(如果有)。

如果对于任何 Xi 都不存在这样的聚合元素 ei,则聚合推演候选不被添加到集合中。聚合推导候选如上从假设的构造函数 C(T1,...,Tn)

  • 如果 ei 是数组类型且 xi 是花括号初始化列表或字符串文字,则 Ti 是对 ei 声明类型的右值引用

在我的示例中,x1 是一个花括号初始化列表({1,2}),而 e1 的类型是数组类型 T[2] ,因此构造函数应该是C(T(&amp;&amp;)[2]) 的形式,并且模板参数可以从{1,2} 中推导出T(&amp;&amp;)[2],根据temp.deduct.call#1

为什么上面的例子被 GCC 拒绝了? GCC 而接受大括号省略方式?如何解释这个例子?这算是 GCC 的错误还是我误解的东西?


我认为奇怪的另一个问题是,如果 Xi 是一个应该用于初始化子聚合的花括号初始化列表,如果项目符号 [1.5] 为真,那么 Xi 将用于初始化子聚合的元素。什么意思?

更新

p2082r1 进一步挖掘之后。从其上下文来看,措辞聚合元素似乎是指聚合类型的元素,而不是聚合的元素。 IIUC,如果满足子弹 [1.5]、[1.6]、[1.7],则 ei 将是聚合元素。但是,如果这些项目符号都不符合,那么 ei 会是什么?此处似乎未指定。

【问题讨论】:

  • clang 拒绝两者,MSVC 都接受godbolt.org/z/brWM388zc,因此 3 个主要编译器都给出 3 个不同的结果。不错。
  • @bolov 我不觉得 Clang 拒绝这个例子很奇怪,因为 Clang 不完全支持 c++20。因此,我没有在我的问题中提到 Clang。
  • This GCC test from their testsuite 包含几乎相同的代码,除了数组大小也是一个模板参数 - 在这种情况下它编译成功。
  • @interjay 具有值依赖绑定的数组类型是我想谈论的另一个case。我不知道子弹 [1.5] 是否被认为是真的?如果是真的,那么ei将是数组的元素,其中数组是C的子聚合。看起来有点晦涩。
  • @MarkusLenger Clang 与 c++20 不完全兼容。我使用最新的 GCC 来测试这样的代码。以及上面第一条评论中链接中的这些编译器也可以编译这段代码。

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


【解决方案1】:

P2082r1 中进一步挖掘,我认为 GCC 在这个例子中是正确的。由于Test的子聚合T t[2]有一个非依赖值绑定数组,因此对应的e0和e1t0,@987654325 @, 分别。因此派生指南是template&lt;typename T&gt; Test(T, T)

当本指南用作假设类类型的构造函数以参与重载解析时,此处应用 [over.match.list] 因为初始化程序是 {{1,2}},因为没有初始化程序列表构造函数,这里使用初始化列表的元素作为参数,如前所述,候选 template&lt;typename T&gt; Test(T, T) 有两个参数,但只有一个参数 ({1,2}) 提供,因此重载决议失败,因此程序格式错误.

不过,我还是觉得这里的规则不是很清楚,应该改进一下,让意思更明确。

【讨论】:

    【解决方案2】:

    这是 GCC 11.2 之前的一个错误,在 GCC 11.3 中已修复。请参阅https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101344 了解导致此错误的详细说明。

    即使是早期版本的 GCC 也可以编译类似的甚至更困难的案例 A(正如问题的 cmets 中提到的那样):

    template<typename T, int N>
    struct A { T t[N]; };
    
    template<typename T>
    struct B { T t[2]; };
    
    int main()
    {
       [[maybe_unused]] A a{{1, 2}}; //ok in GCC
       [[maybe_unused]] B b{{1, 2}}; //ok in GCC >= 11.3
    }
    

    演示:https://gcc.godbolt.org/z/4rcYj6fah

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-07-29
    • 2013-06-03
    • 2012-07-28
    • 2018-03-06
    • 2017-09-10
    • 2013-09-25
    • 2012-11-21
    相关资源
    最近更新 更多