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

jac*_*k X 25 c++ language-lawyer c++20

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

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

#1被GCC拒绝,而#2会被 GCC 接受。

根据over.match.class.deduct#1

此外,如果定义了 C 并且它的定义满足聚合类([dcl.init.aggr])的条件,假设任何依赖基类都没有虚函数和虚基类,并且初始化器是一个非-empty 花括号初始化列表或括号表达式列表,并且没有 C 的推导指南,该集合包含一个额外的函数模板,称为聚合推导候选,定义如下。令 X 1 ,...,X N是花括号初始化器列表或表达式列表的初始化器列表或指定初始化器列表的元素。对于每个 X i,让 e i是 C 或其(可能是递归的)子聚合之一的相应聚合元素,这些子聚合将由 X 初始化 如果

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

如果对于任何 X i没有这样的聚合元素 e i,则不将聚合推导候选添加到集合中。聚合推导候选如上从假设构造函数 C(T 1 ,...,T n )导出

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

在我的例子中,x 1是一个花括号初始化列表( {1,2}) 并且 e 1的类型是数组类型 T[2],因此构造函数应该是形式C(T(&&)[2]),模板参数可以T(&&)[2]{1,2}as per temp推导出来.deduct.call#1

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


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

更新

在进一步深入p2082r1 之后。从上下文来看,聚合元素这个词似乎意味着聚合类型的元素而不是聚合的元素。IIUC,如果子弹[1.5]、[1.6]、[1.7]满足,e i将是聚合元素。但是,如果这些子弹都不符合,那么 e i会是什么?这里似乎没有指定。

Fed*_*dor 1

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

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

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
}
Run Code Online (Sandbox Code Playgroud)

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