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

Posted

技术标签:

【中文标题】为啥 C++ 列表初始化也会考虑常规构造函数?【英文标题】:Why does C++ list initialization also take regular constructors into account?为什么 C++ 列表初始化也会考虑常规构造函数? 【发布时间】:2017-12-28 09:31:45 【问题描述】:

在 C++ 中,当使用 initializer_list 语法初始化对象时,当没有其他列表初始化规则适用时,对象的常规构造函数也参与重载决策。 据我了解,以下代码调用 X::X(int)

class X  int a_; X(int a):a_(a)  );

void foo() 
   X bar3;

但我不明白,为什么在 initializer_lists 的上下文中也考虑常规构造函数。我感觉现在很多程序员写 X3 来调用构造函数,而不是 X(3) 来调用构造函数。 我根本不喜欢这种风格,因为它让我觉得对象没有常规的构造函数。

initializer_list 语法也可以用来调用常规构造函数的原因是什么?现在有理由比常规构造函数调用更喜欢这种语法吗?

【问题讨论】:

“我一点也不喜欢这种风格,因为它让我觉得对象没有常规的构造函数” - 我不会称之为语言问题. 原因是他们想让这种语法几乎适用于任何类型。你会发现它也适用于 int 和数组。如果旧的语法适合您,那么没有真正的理由更喜欢旧的语法而不是久经考验的语法。 现在很多程序员都写 X3 是的,它是统一初始化。 【参考方案1】:

基本上是一团糟。对于 C++11,它试图创建一种统一的方法来初始化对象,而不是其他必要的多种方法:

T v(args...); 一般情况下 T d = T(); 使用基于堆栈的对象的默认构造函数时 T m((iterator(x)), iterator()); 与 Most Vexing Parse 战斗(注意第一个参数周围的额外括号) 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> v11, 2; // two elements: 1 and 2

tl;dr:初始化列表和统一初始化语法是两个独立的符号。可悲的是,它们发生了冲突。

【讨论】:

vu 1, 2, 3 ; 对于使用 std::initializer_list 的 c'tor 来说是不可接受的,但对于像 std::array 这样的聚合则完全没问题。谈论历史性错误。 读到这里让我很难过。特别是因为1, 2 没有类型,但在auto l = 1, 2;lstd::initializer_list&lt;int&gt;。让我想知道std::initializer_list 解决了什么问题 我真的很讨厌这个。如果他们只为初始化列表做一些需要双括号的事情,那么整个事情就会变得更加简洁。【参考方案2】:

我一点也不喜欢这种风格,因为它让我觉得对象没有常规的构造函数。

如果是聚合类型,则执行聚合初始化。否则,它会考虑构造函数。如果它让你认为这个类是一个聚合,那不是语言问题。

现在有理由比常规构造函数调用更喜欢这种语法吗?

如果您是统一初始化的支持者,可以。如果你不是,你可以坚持旧的风格。请注意,另一个答案是关于std::initializer_list,但这不适用于您的问题,因为您没有采用std::initializer_list 的构造函数。 Braced-init-lists 和 std::initializer_list 是不同的概念。最好不要让他们这么早就弄糊涂了。

【讨论】:

以上是关于为啥 C++ 列表初始化也会考虑常规构造函数?的主要内容,如果未能解决你的问题,请参考以下文章

C++:考虑但不调用构造函数的特殊性

为啥我不能访问派生构造函数的成员初始化列表中继承的受保护字段?

为啥在使用大括号初始值设定项列表时首选 std::initializer_list 构造函数?

C++类编程

C++ 构造函数初始化列表

为啥 C++ 中 std::mutex 的构造函数不抛出?