初始化列表中的空括号魔法
Empty braces magic in initializer lists
考虑以下最小示例:
#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
}
我对a、b、c没有问题,一切都比较清楚了
但我不明白为什么 d 有效
d 中这对额外的大括号代表什么?我查阅了 C++20 标准,但我无法轻易找到答案。 clang 和 gcc 都同意这段代码,所以是我错过了一些东西
获取有关编译器功能的信息的一个好技巧是使用所有错误进行编译:
-Weverything
。让我们在这里查看输出(仅限 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?
^~~~~~
X::X(std::initializer_list)
被调用。
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.
std::initializer_list
从 {0}
初始化。所以我们有 X d{std::initializer_list<int>{0}};
!
这向我们展示了我们需要的一切。额外的括号用于构造初始化列表。
注意:如果你想添加额外的括号,你可以通过调用 copy/move 构造函数(或省略它),但 C++ 编译器不会隐式地这样做为您防止错误:
X d{X{{{}}}}; // OK
X e{{{{}}}}; // ERROR
以为我只是举例说明:
X d{ { {} }};
| | |
construct an | |
`X` from ... an initializer_list |
containing... int{}
列表初始化的规则是找到一个 initializer_list<T>
构造函数并尽可能使用它,否则...枚举构造函数并执行正常操作。
与X{{}}
,即列表初始化:最外层的{}
是initializer_list
,其中包含一个元素:{}
,即0
。足够直截了当(虽然含糊)。
但是对于 X{{{}}}
,这不再适用于使用最外层的 {}
作为 initializer_list
,因为您无法从 [=21] 初始化 int
=].所以我们回退到使用构造函数。现在,其中一个构造函数采用 initializer_list
,所以这有点像重新开始,只是我们已经剥掉了一层括号。
这就是为什么 vector<int>{{1, 2, 3}}
也有效,而不仅仅是 vector<int>{1, 2, 3}
。但是喜欢...不要。
考虑以下最小示例:
#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
}
我对a、b、c没有问题,一切都比较清楚了
但我不明白为什么 d 有效
d 中这对额外的大括号代表什么?我查阅了 C++20 标准,但我无法轻易找到答案。 clang 和 gcc 都同意这段代码,所以是我错过了一些东西
获取有关编译器功能的信息的一个好技巧是使用所有错误进行编译:
-Weverything
。让我们在这里查看输出(仅限 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?
^~~~~~
X::X(std::initializer_list)
被调用。
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.
std::initializer_list
从 {0}
初始化。所以我们有 X d{std::initializer_list<int>{0}};
!
这向我们展示了我们需要的一切。额外的括号用于构造初始化列表。
注意:如果你想添加额外的括号,你可以通过调用 copy/move 构造函数(或省略它),但 C++ 编译器不会隐式地这样做为您防止错误:
X d{X{{{}}}}; // OK
X e{{{{}}}}; // ERROR
以为我只是举例说明:
X d{ { {} }};
| | |
construct an | |
`X` from ... an initializer_list |
containing... int{}
列表初始化的规则是找到一个 initializer_list<T>
构造函数并尽可能使用它,否则...枚举构造函数并执行正常操作。
与X{{}}
,即列表初始化:最外层的{}
是initializer_list
,其中包含一个元素:{}
,即0
。足够直截了当(虽然含糊)。
但是对于 X{{{}}}
,这不再适用于使用最外层的 {}
作为 initializer_list
,因为您无法从 [=21] 初始化 int
=].所以我们回退到使用构造函数。现在,其中一个构造函数采用 initializer_list
,所以这有点像重新开始,只是我们已经剥掉了一层括号。
这就是为什么 vector<int>{{1, 2, 3}}
也有效,而不仅仅是 vector<int>{1, 2, 3}
。但是喜欢...不要。