“{}”语法在多大程度上具有通用性?

6
谈论初始化列表中,我理解Stroustrup基本上是在说使用花括号的新构造语法应该成为所有先前构造语法的通用替代品。
X x1(); // most vexing parse ... doesn't work as intended
X x2(x1);
X x3 = x1;
X x4 = X();

相反,新的语法应该被统一使用作为可能的替代方案,你可以在任何情况下使用...这是我从他的演讲中得出的核心信息。也许我误解了他。
那么问题是,这种语法有多通用?在新的C++11代码中,是否有可能永远不使用旧式构造,或者是否存在必须回退的情况?
当我遇到以下错误时,我相信这是编译器中的错误(但如果有改正意见,我会很高兴接受)。
struct X {};

int main() {
  X x;
  X& y{x}; // works with (x)
  X& z{y}; // works with (y)
}

这段代码在 g++ 4.7.1 上无法编译,也无法在 ideone 的 4.5.1 上编译。

prog.cpp: In function 'int main()':
prog.cpp:5:9: error: invalid initialization of non-const reference of type 'X&' from an rvalue of type '<brace-enclosed initializer list>'
prog.cpp:6:9: error: invalid initialization of non-const reference of type 'X&' from an rvalue of type '<brace-enclosed initializer list>'

请注意,当我将X替换为int时,它有效。

新的花括号构造语法并不是所有以前构造语法的通用替代品。不幸的是,这是一个非常普遍的误解。 - Mooing Duck
1个回答

9

花括号初始化可以在任何初始化器使用的地方使用。有时候你必须使用圆括号才能访问一个花括号初始化器无法访问的构造函数,但这种情况很少见。

std::vector<int> v(1,1);
std::vector<int> v{1,1};

vector<int>有一个特化的构造函数,可以接受两个int参数,因此在尝试构造长度为两个int的向量时会产生歧义。这个模糊的构造函数仅存在于向后兼容方面。不应该定义任何与初始化器列表发生冲突的构造函数。

通过花括号初始化语法,模糊性得以解决,它优先选择初始化器列表构造函数而不是其他可能匹配的构造函数。如果希望在使用非初始化器列表构造函数的情况下解决歧义,则不能使用花括号初始化。


Bjarne Stroustrup写道

只有在C++11中才能统一使用{}初始化,因此旧的C++代码使用()和=初始化。因此,()和=可能更为熟悉。但是,除了需要区分元素列表初始化和构造函数参数列表的罕见情况外,我不知道有任何逻辑上首选()符号的理由。

                                                         ——《C++程序设计语言(第四版)》§17.3.2.1


您的示例代码是完全合法的,并且应如预期地工作。您遇到的错误只是GCC中的一个错误。Clang和VC++ 2012都接受此代码。


非常好的观点。这可以通过将第二个版本定义为std::vector<int> v{{1,1}};来修复。 - bitmask
2
@bitmask 这些情况并不罕见,因为初始化列表构造函数优先于所有其他构造函数。相关问题请参考这里 - juanchopanza
3
我不认为那会有所改进。这只会改变你需要使用的语法来指示是否偏好initializer_list构造函数。基本上,花括号将完全等同于圆括号,并且在任一情况下使用初始化列表都需要一个嵌套的花括号集,这意味着语法与数组初始化不一致。 - bames53
@bames53:没错,我没有考虑到数组。 - bitmask
在我的理解中,我使用{}来从值列表(包括一个或零个值)进行初始化,并使用()来表示其他所有构造函数。这样可以消除所有的歧义。 - Mooing Duck
@MooingDuck 那个可以。我更喜欢始终使用 {},避免使用或编写不能通过花括号初始化使用的构造函数。 - bames53

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接