为什么C++列表初始化也考虑常规构造函数?

Why does C++ list initialization also take regular constructors into account?

本文关键字:常规 构造函数 C++ 列表 初始化 为什么      更新时间:2023-10-16

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是单独的概念。最好不要让他们这么早就感到困惑。