【发布时间】: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 接受。
此外,如果定义了 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(&&)[2]) 的形式,并且模板参数可以从{1,2} 中推导出T(&&)[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