总是用{}初始化对象是个好习惯吗

Is it good habit to always initialize objects with {}?

本文关键字:好习惯 对象 初始化      更新时间:2023-10-16

使用新的{}语法初始化对象,如下所示:

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.在下面的评论中指出的,我对带有大括号初始化的autoC++17规则的理解仍然是不太正确。)所有这些行为都有点令人惊讶,(在我和Scott Meyers看来)令人讨厌和困惑
  • 101010列出的第三个好处是,初始化列表构造函数比所有其他构造函数都更受欢迎,这实际上是一个缺点也是一个好处。您已经提到std::vector<int> vi{ 5, 1 };的行为对于熟悉vector旧的双元素构造函数的人来说是令人惊讶的。Scott Meyers在Effective Modern C++中列出了一些其他例子。就我个人而言,我发现这比auto的推断行为更糟糕(我通常只将auto与副本初始化一起使用,这使得第一个问题很容易避免)

EDIT:事实证明,愚蠢的编译器实现决策有时可能是使用大括号初始化的另一个原因(尽管实际上#undef方法可能更正确)。

使用列表初始化对象初始化在适用的情况下应优先考虑,因为:

  1. 其他好处列表初始化限制了允许的隐式缩小转换。

    特别是它禁止:

    • 从浮点类型转换为整数类型
    • long doubledouble或到float的转换以及从doublefloat的转换,除非源是常量表达式和溢出不会发生
    • 从整数类型到浮点类型的转换,除非源是可以存储其值的常量表达式正好在目标类型中
    • 从整数或无范围枚举类型转换为整数类型,该类型不能表示原始枚举的所有值,除了其中source是可以存储其值的常量表达式正好在目标类型中
  2. 另一个好处是,它不受大多数烦恼的影响解析
  3. 此外,初始化列表构造函数比其他构造函数更受欢迎可用构造函数,默认构造函数除外
  4. 此外,它们广泛可用,所有STL容器都有初始化列表构造函数

关于你的例子,我想说,知识带来力量。有一个特定的构造函数用于生成一个5个1的向量(即std::vector<int> vi( 5, 1);)。