始终使用 {} 初始化对象是个好习惯吗?
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 初始化对象
初始化
在适用的地方应该是首选,因为:
还有其他好处
列表初始化限制了允许的隐式缩小
转换。
特别禁止:
- 从浮点类型到整数类型的转换
- 从
long double
到 double
或到 float
的转换以及从 double
到 float
的转换,除非源是
不会发生常量表达式和溢出。
- 从整数类型到浮点类型的转换,除非源是常量表达式,其值可以存储
完全符合目标类型。
- 从整数或无范围枚举类型转换为不能表示原始所有值的整数类型,除了
其中 source 是一个常量表达式,其值可以存储
完全符合目标类型。
- 另一个好处是可以免疫最烦人的事情
解析.
- 此外,初始化列表构造函数优于其他构造函数
可用的构造函数,默认构造函数除外。
- 此外,它们广泛可用,所有 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 指出的那样,在下面的评论中,我对带有大括号初始化的 auto
的 C++17
规则的理解是 still 不太正确.) 所有这些行为都有些令人惊讶,并且(在我和 Scott Meyers 看来)令人讨厌和困惑。
- 101010 列出的第三个好处,即初始化列表构造函数优于所有其他构造函数,这实际上是缺点也是优点。您已经提到
std::vector<int> vi{ 5, 1 };
的行为令熟悉 vector
的旧二元素构造函数的人感到惊讶。 Scott Meyers 在 Effective Modern C++ 中列出了一些其他示例。就个人而言,我发现这比 auto
推导行为更糟糕(我通常只使用 auto
进行复制初始化,这使得第一个问题很容易避免)。
编辑: 事实证明,愚蠢的编译器实现决策有时可能是 (尽管实际上 #undef
方法可能更正确)。
使用新的 {} 语法初始化对象,如下所示:
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 初始化对象 初始化 在适用的地方应该是首选,因为:
还有其他好处 列表初始化限制了允许的隐式缩小 转换。
特别禁止:
- 从浮点类型到整数类型的转换
- 从
long double
到double
或到float
的转换以及从double
到float
的转换,除非源是 不会发生常量表达式和溢出。 - 从整数类型到浮点类型的转换,除非源是常量表达式,其值可以存储 完全符合目标类型。
- 从整数或无范围枚举类型转换为不能表示原始所有值的整数类型,除了 其中 source 是一个常量表达式,其值可以存储 完全符合目标类型。
- 另一个好处是可以免疫最烦人的事情 解析.
- 此外,初始化列表构造函数优于其他构造函数 可用的构造函数,默认构造函数除外。
- 此外,它们广泛可用,所有 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 指出的那样,在下面的评论中,我对带有大括号初始化的auto
的C++17
规则的理解是 still 不太正确.) 所有这些行为都有些令人惊讶,并且(在我和 Scott Meyers 看来)令人讨厌和困惑。 - 101010 列出的第三个好处,即初始化列表构造函数优于所有其他构造函数,这实际上是缺点也是优点。您已经提到
std::vector<int> vi{ 5, 1 };
的行为令熟悉vector
的旧二元素构造函数的人感到惊讶。 Scott Meyers 在 Effective Modern C++ 中列出了一些其他示例。就个人而言,我发现这比auto
推导行为更糟糕(我通常只使用auto
进行复制初始化,这使得第一个问题很容易避免)。
编辑: 事实证明,愚蠢的编译器实现决策有时可能是 #undef
方法可能更正确)。