使用新的{}语法初始化对象,如下所示:
int a { 123 };
有好处-你不会声明一个函数,而不是错误地创建一个变量。我甚至听说总是这样做应该是一种习惯。但看看会发生什么:
// I want to create vector with 5 ones in it:
std::vector<int> vi{ 5, 1 }; // ups we have vector with 5 and 1.
这是个好习惯吗?有没有办法避免这样的问题?
坦率地说,各种初始化技术的微妙之处使得很难说任何一种练习都是"好习惯"
正如评论中提到的,Scott Meyers在Modern Effective C++中详细讨论了大括号初始化。他在他的博客上对此事发表了进一步的评论,例如在这里和这里。在第二篇文章中,他最后明确表示,他认为C++初始化的变幻莫测只是糟糕的语言设计。
正如101010的回答中所提到的,支架初始化有好处。在我看来,防止隐性狭窄是主要的好处。"最麻烦的解析"问题当然是一个真正的好处,但它微不足道——在我看来,在大多数情况下,可能会在编译时发现不正确的int a();
而不是int a;
。
但至少有两个主要缺点:
- 在C++11和C++14中,
auto
总是从大括号初始值设定项推导std::initializer_list
。在C++17中,如果初始化列表中只有一个元素,并且=
是而不是,则auto
推导出该元素的类型多个元素的行为是不变的(请参阅上面链接的第二篇博客文章,以获得更清晰的解释,并附上示例。)(编辑:正如T.C.在下面的评论中指出的,我对带有大括号初始化的auto
的C++17
规则的理解仍然是不太正确。)所有这些行为都有点令人惊讶,(在我和Scott Meyers看来)令人讨厌和困惑 - 101010列出的第三个好处是,初始化列表构造函数比所有其他构造函数都更受欢迎,这实际上是一个缺点也是一个好处。您已经提到
std::vector<int> vi{ 5, 1 };
的行为对于熟悉vector
旧的双元素构造函数的人来说是令人惊讶的。Scott Meyers在Effective Modern C++中列出了一些其他例子。就我个人而言,我发现这比auto
的推断行为更糟糕(我通常只将auto
与副本初始化一起使用,这使得第一个问题很容易避免)
EDIT:事实证明,愚蠢的编译器实现决策有时可能是使用大括号初始化的另一个原因(尽管实际上#undef
方法可能更正确)。
使用列表初始化对象初始化在适用的情况下应优先考虑,因为:
-
其他好处列表初始化限制了允许的隐式缩小转换。
特别是它禁止:
- 从浮点类型转换为整数类型
- 从
long double
到double
或到float
的转换以及从double
到float
的转换,除非源是常量表达式和溢出不会发生 - 从整数类型到浮点类型的转换,除非源是可以存储其值的常量表达式正好在目标类型中
- 从整数或无范围枚举类型转换为整数类型,该类型不能表示原始枚举的所有值,除了其中source是可以存储其值的常量表达式正好在目标类型中
- 另一个好处是,它不受大多数烦恼的影响解析
- 此外,初始化列表构造函数比其他构造函数更受欢迎可用构造函数,默认构造函数除外
- 此外,它们广泛可用,所有STL容器都有初始化列表构造函数
关于你的例子,我想说,知识带来力量。有一个特定的构造函数用于生成一个5个1的向量(即std::vector<int> vi( 5, 1);
)。