在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)来调用construcor。 我根本不喜欢这种风格,因为它让我觉得这个对象没有常规的构造函数。
为什么initializer_list语法也可用于调用常规构造函数的原因是什么?是否有理由比常规构造函数调用更喜欢这种语法?
答案 0 :(得分:18)
基本上这是一个混乱。对于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> v1{1, 2}; // two elements: 1 and 2
tl; dr:初始化列表和统一初始化语法是两个单独的表示法。可悲的是,他们发生冲突。
答案 1 :(得分:1)
我根本不喜欢这种风格,因为它让我觉得这个对象没有常规的构造函数。
如果它是聚合类型,则执行聚合初始化。否则,它会考虑构造函数。如果它让你认为该类是聚合,那不是语言问题。
现在是否有理由比常规构造函数调用更喜欢这种语法?
如果你是统一初始化的支持者,是的。如果你不是,你可以坚持旧的风格。请注意,另一个答案是关于std::initializer_list
,但这并不适用于您的问题,因为您没有构造函数需要std::initializer_list
。 Braced-init-list 和std::initializer_list
是不同的概念。最好不要在早期让他们感到困惑。