在现代C ++(C ++ 11及以上版本)中正确初始化variables,使用()或{}?
C ++参考页面指出()用于值初始化,{}用于值和聚合和列表初始化。 所以,如果我只想要初始化值,我使用哪一个? () 要么 {}? 我在问,因为在Bjarne本人的“C ++游览”一书中,他似乎更喜欢使用{},甚至对于值初始化(例如参见第6页和第7页),所以我认为总是很好的做法使用{},即使是值初始化。 不过 ,最近我被下面的bug吓坏了。 考虑下面的代码。
auto p = std::make_shared<int>(3); auto q{ p }; auto r(p);
现在根据编译器(Visual Studio 2013), q
typesstd::initializer_list<std::shared_ptr<int>>
,这不是我的意图。 我实际上想要的q
实际上是r
是什么,这是std::shared_ptr<int>
。 所以在这种情况下,我不应该使用{}来进行值初始化,而是使用()。 鉴于此,为什么Bjarne在他的书中似乎仍然倾向于使用{}来进行值初始化? 例如,他在第6页的底部使用了double d2{2.3}
。
为了明确地回答我的问题,我应该什么时候使用()以及何时使用{}? 这是语法正确还是编程实践的问题?
哦,呃,如果可能的话请用简单的英语。
编辑:这似乎是我稍微误解了价值初始化 (见下面的答案)。 但是,上述问题仍然存在。
这是我的意见。
当使用auto
作为types说明符时,它使用更简洁:
auto q = p; // Type of q is same as type of p auto r = {p}; // Type of r is std::initializer_list<...>
使用显式types说明符时,最好使用{}
而不是()
。
int a{}; // Value initialized to 0 int b(); // Declares a function (the most vexing parse)
可以使用
int a = 0; // Value initialized to 0
但是,forms
int a{};
也可以用来初始化用户定义types的对象。 例如
struct Foo { int a; double b; }; Foo f1 = 0; // Not allowed. Foo f1{}; // Zero initialized.
Scott Meyers在他的着作“ 有效的现代C ++ ”( Effective Modern C ++)中有两个初始化方法的区别。
他总结了这两种方法:
大多数开发人员最终select一种分隔符作为默认值,只有在必须时才使用另一种分隔符。 大括号默认的人吸引他们无与伦比的广泛的适用性,禁止缩小转换,并免除C ++的最令人头痛的parsing。 这样的人理解,在某些情况下(例如,创build一个具有给定大小和初始元素值的
std::vector
),括号是必需的。 另一方面,去括号的人群将圆括号作为默认的参数分隔符。 它们被C ++ 98语法传统的一致性所吸引,它避免了auto-deduced-a-std :: initializer_list问题,并且知道它们的对象创build调用不会被无意中由std::initializer_list
构造函数。 他们承认,有时候只有大括号会做(例如,创build一个具有特定值的容器时)。 没有一致的意见,任何一种方法都比另一种更好,所以我的build议是select一个并持续应用。
首先,似乎有一个术语混淆。 你有什么不是价值初始化。 当你不提供任何明确的初始化参数时,就会发生值初始化。 int x;
使用默认初始化, x
的值将是未指定的。 int x{};
使用值初始化, x
将为0
。 int x();
声明一个函数 – 这就是为什么{}
对于值初始化是首选。
您显示的代码不使用值初始化。 使用auto
,最安全的是使用复制初始化:
auto q = p;
还有一个重要的区别:大括号初始值设定项要求给定的types实际上可以保存给定的值。 换句话说,它禁止缩小价值,如舍入或截断。
int a(2.3); // ok? a will hold the value 2, no error, maybe compiler warning uint8_t c(256); // ok? the compiler should warn about something fishy going on
相比大括号初始化
int A{2.3}; // compiler error, because int can NOT hold a floating point value double B{2.3}; // ok, double can hold this value uint8_t C{256}; // compiler error, because 8bit is not wide enough for this number
特别是在使用模板进行generics编程时,您应该使用大括号初始化来避免底层types对input值做出意想不到的令人讨厌的惊喜。
如果为空,{}是值初始化,如果不是,则为列表/聚合初始化。
从草稿中, 7.1.6.4自动说明符,7 / …例子 ,
auto x1 = { 1, 2 }; // decltype(x1) is std::initializer_list<int>
规则在这里解释有点复杂(甚至很难从源代码读取!)。
Herb Sutter似乎在CppCon 2014(参与讨论的39:25)中使用了自动和大括号初始化器,如下所示:
auto x = MyType { initializers };
每当你想强制这个types时,在定义中从左到右是一致的:
- types推导:
auto x = getSomething()
- 强制types:
auto x = MyType { blah }
- 用户定义文字
auto x = "Hello, world."s
- 函数声明:
auto f { some; commands; } -> MyType
auto f { some; commands; } -> MyType
- 命名为Lambda:
using auto f = [=]( { some; commands; } -> MyType
- C ++ 11样式的typedef:
using AnotherType = SomeTemplate<MyTemplateArg>
Scott Mayers刚刚发布了一篇相关的博客文章“C ++初始化的迷思”(Thinking on the Vagaries of C ++ Initialization) 。 看来C ++在实现一个真正统一的初始化语法之前还有一段路要走。