为什么C++列表初始化也考虑常规构造函数?
Why does C++ list initialization also take regular constructors into account?
C++当使用initializer_list语法初始化对象时,当没有其他列表初始化规则应用时,对象的常规构造函数也会参与重载解析。 据我了解,以下代码调用 X::X(int)
class X { int a_; X(int a):a_(a) {} );
void foo() {
X bar{3};
}
但我不明白,为什么在initializer_lists的上下文中也考虑常规构造函数。我觉得现在很多程序员写 X{3} 来调用构造函数,而不是 X(3) 来调用构造函数。 我根本不喜欢这种风格,因为它让我认为对象没有常规构造函数。
initializer_list语法也可以用来调用正则构造函数的原因是什么?现在有理由更喜欢这种语法而不是常规构造函数调用吗?
本质上是一团糟。对于 C++11,尝试创建一种统一的方式来初始化对象,而不是多种方法,否则需要:
T v(args...);
通常的情况
对- 基于堆栈的对象使用默认构造函数时
T d = T();
T m((iterator(x)), iterator());
对抗最令人烦恼的解析(注意第一个参数周围的额外括号)- 聚合初始化
T a = { /* some structured values */ };
相反,统一初始化语法被发明出来:
T u{ /* whatever */ };
目的是统一初始化语法将在任何地方使用,旧的 stule 将过时。一切都很好,除了std::initializer_list<S>
初始化的支持者意识到语法将是这样的:
std::vector<int> vt({ 1, 2, 3 });
std::vector<int> vu{{ 1, 2, 3 }};
这被认为是不可接受的,统一的初始化语法受到了不可挽回的损害,以允许如此更好的
std::vector<int> vx{ 1, 2, 3 };
这种混合的问题在于,现在有时不清楚实际含义,统一的初始化语法不再统一。在某些情况下,它仍然是必要的(特别是在通用代码中对基于堆栈的对象进行值初始化),但它并非在所有情况下都是正确的选择。例如,以下两个符号的意思相同,但它们没有:
std::vector<int> v0(1, 2); // one element with value 2
std::vector<int> v1{1, 2}; // two elements: 1 and 2
tl;DR:初始值设定项列表和统一初始化语法是两个单独的表示法。可悲的是,它们相互冲突。
我根本不喜欢这种风格,因为它让我认为对象没有常规构造函数。
如果是聚合类型,则执行聚合初始化。否则,它将考虑构造函数。如果它让你认为类是一个聚合,那不是语言问题。
现在有理由更喜欢这种语法而不是常规构造函数调用吗?
如果您是统一初始化的支持者,是的。如果你不是,你可以坚持旧风格。请注意,另一个答案谈到了std::initializer_list
但这根本不适用于您的问题,因为您没有需要std::initializer_list
的构造函数。大括号初始化列表s 和std::initializer_list
是单独的概念。最好不要让他们这么早就感到困惑。
- "error: no matching function for call to"构造函数错误
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- 选择要调用的构造函数
- 如何委托派生类使用其父构造函数?
- 构造函数正在调用一个使用当前类类型的函数
- 没有用于初始化C++中的变量模板的匹配构造函数
- 初始化具有非默认构造函数的std::数组项的更好方法
- 当从函数参数中的临时值调用复制构造函数时
- 在c++构造函数中使用随机字符串生成器
- 一对向量构造函数:初始值设定项列表与显式构造
- 从构造函数抛出异常时如何克服内存泄漏
- 是否有任何常规方法可以通知 STL 移动和复制构造函数?
- 如何初始化堆,以便静态构造函数可以在常规 MFC dll 中使用堆?
- 为什么C++列表初始化也考虑常规构造函数?
- C++中的错误处理,构造函数与常规方法
- 如何使用常规构造函数模式初始化 C++ 11 标准容器
- 使用常规构造函数进行聚合初始化