始终使用 {} 初始化对象是个好习惯吗?

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

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

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.

那这是好习惯吗?有没有办法避免这样的问题?

正在使用 list 初始化对象 初始化 在适用的地方应该是首选,因为:

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

    特别禁止:

    • 从浮点类型到整数类型的转换
    • long doubledouble 或到 float 的转换以及从 doublefloat 的转换,除非源是 不会发生常量表达式和溢出。
    • 从整数类型到浮点类型的转换,除非源是常量表达式,其值可以存储 完全符合目标类型。
    • 从整数或无范围枚举类型转换为不能表示原始所有值的整数类型,除了 其中 source 是一个常量表达式,其值可以存储 完全符合目标类型。
  2. 另一个好处是可以免疫最烦人的事情 解析.
  3. 此外,初始化列表构造函数优于其他构造函数 可用的构造函数,默认构造函数除外。
  4. 此外,它们广泛可用,所有 STL 容器都有初始化列表构造函数。

现在关于你的例子,我想说知识就是力量。有一个特定的构造函数用于制作 5 个向量(即 std::vector<int> vi( 5, 1);)。

坦率地说,各种初始化技术的微妙之处,很难说任何一种做法都是"good habit."

如评论中所述,Scott Meyers 在 Modern Effective C++ 中详细讨论了大括号初始化。他在他的博客上对此事做了进一步的评论,例如here and here。在那一秒 post,他终于明确表示,他认为 C++ 初始化变幻莫测的泥潭简直就是糟糕的语言设计。

如 101010 的回答所述,大括号初始化有好处。在我看来,防止隐式缩小是主要好处。 "most vexing parse" 问题当然是一个真正的好处,但它微不足道——在我看来,在大多数情况下,一个不正确的 int a(); 而不是 int a; 可能会在编译时被捕获。

但至少有两个主要缺点:

  • 在 C++11 和 C++14 中,auto 总是从大括号初始化器推导出 std::initializer_list。在 C++17 中,如果初始化列表中只有一个元素,并且 = 而不是 使用,则 auto 会推导出该元素的类型; 多个元素的行为没有改变(请参阅上面链接的第二篇博客 post 以获得更清晰的解释和示例。)(编辑: 正如 T.C 指出的那样,在下面的评论中,我对带有大括号初始化的 autoC++17 规则的理解是 still 不太正确.) 所有这些行为都有些令人惊讶,并且(在我和 Scott Meyers 看来)令人讨厌和困惑。
  • 101010 列出的第三个好处,即初始化列表构造函数优于所有其他构造函数,这实际上是缺点也是优点。您已经提到 std::vector<int> vi{ 5, 1 }; 的行为令熟悉 vector 的旧二元素构造函数的人感到惊讶。 Scott Meyers 在 Effective Modern C++ 中列出了一些其他示例。就个人而言,我发现这比 auto 推导行为更糟糕(我通常只使用 auto 进行复制初始化,这使得第一个问题很容易避免)。

编辑: 事实证明,愚蠢的编译器实现决策有时可能是 (尽管实际上 #undef 方法可能更正确)。