初始值设定项列表中的空大括号魔术



考虑以下最小示例:

#include <iostream>
struct X {
X() { std::cout << "Default-ctor" << std::endl; }
X(std::initializer_list<int> l) { 
std::cout << "Ilist-ctor: " << l.size() << std::endl; 
}
};
int main() {
X a{};
X b({}); // reads as construct from {}
X c{{}}; // reads as construct from {0}
X d{{{}}}; // reads as construct from what?
// X e{{{{}}}}; // fails as expected
}

Godbolt示例

我对a、b和c没有任何疑问,一切都很清楚

但我不明白为什么d工作

d中的这对额外的背带代表什么?我查了一下C++20标准,但很难找到答案。clang和gcc都同意这个代码,所以是我错过了

要获得有关编译器功能的信息,一个很好的技巧是使用所有错误进行编译:CCD_ 1。让我们在这里看到输出(仅适用于d(:

9.cpp:16:6: warning: constructor call from initializer list is incompatible with C++98                                                                                            
[-Wc++98-compat]                                                                                                                                                            
X d{{{}}}; // reads as construct from what?                                                                                                                                     
^~~~~~                           

调用CCD_ 3。

9.cpp:16:8: warning: scalar initialized from empty initializer list is incompatible with                                                                                          
C++98 [-Wc++98-compat]                                                                                                                                                      
X d{{{}}}; // reads as construct from what?                                                                                                                                     
^~                               

标量(int(在内部{}中初始化。所以我们有X d{{0}}

9.cpp:16:7: warning: initialization of initializer_list object is incompatible with                                                                                               
C++98 [-Wc++98-compat]                                                                                                                                                      
X d{{{}}}; // reads as construct from what?                                                                                                                                     
^~~~                                                                                                                                                                        
5 warnings generated.                                                                                                                                                             

CCD_ 7是从CCD_。所以我们有了X d{std::initializer_list<int>{0}};

这向我们展示了我们所需要的一切。额外的括号用于构造初始值设定项列表。

注意:如果你想添加额外的方括号,你可以通过调用复制/移动构造函数(或删除它(来添加,但C++编译器不会隐式地为你添加方括号以防止错误:

X d{X{{{}}}}; // OK
X e{{{{}}}}; // ERROR

我只想说明一下:

X d{               {                       {}        }};
|               |                       |
construct an    |                       |
`X` from ...    an initializer_list     |
containing...           int{}

列表初始化的规则是找到一个-Weverything0构造函数并在可能的情况下使用它,否则。。。枚举构造函数并执行正常操作。

对于X{{}},即列表初始化:最外层的{}initializer_list,其中包含一个元素:{},即0。够直白的(虽然隐晦(。

但对于X{{{}}},使用最外层的{}作为initializer_list将不再有效,因为无法从{{}}初始化int。因此,我们转而使用构造函数。现在,其中一个构造函数使用initializer_list,所以这有点像重新开始,只是我们已经剥离了一层大括号。


这就是为什么vector<int>{{1, 2, 3}}也能工作,而不仅仅是vector<int>{1, 2, 3}。但就像。。。不要。

最新更新