在现代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), qtypesstd::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将为0int 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 ++在实现一个真正统一的初始化语法之前还有一段路要走。