和许多人一样,我对C++0x感到非常兴奋。我尝试在新项目中学习和使用新功能,这样我就可以编写最好,最易于维护的代码。
毋庸置疑,我喜欢新初始化器背后的想法。所以我在看他们,这些对我来说很有意义:
T x = { 1, 2, 3 }; // like a struct or native array
T x({1, 2, 3}); // copy construct something like an "object literal" in other languages... cool!
return {1, 2, 3}; // similar to above, but returning it, even cooler!
对我来说没有意义的是:
T x{1, 2, 3};
感觉......很奇怪。我不确定人们想要使用哪种语法,这是模仿,它似乎并不“正确”。
这种语法背后的设计/思想是什么?
它似乎有所作为的唯一例子是这样的:
std::vector<int> the_vec{4};
会调用初始化列表构造函数,但为什么不这样写呢:
std::vector<int> the_vec = {4};
做每个人都熟悉的事情?
答案 0 :(得分:26)
这种语法背后的设计/思想是什么?
首先,大括号语法可以避免烦恼的解析:
T x(); // function declaration
T x{}; // value-initialized object of type 'T' named 'x'
在C ++ 03中,您最接近的是T x((T()));
或T x = T();
,这两者都需要T
才能拥有可访问的复制构造函数。
答案 1 :(得分:19)
首先,你真的有两个变体:
T x = { 1, 2, 3 };
T x{1, 2, 3};
这两个实际上正在进行相同的初始化,但是如果它选择explicit
构造函数,则第一个无效。否则它们是相同的。第一个称为“复制列表初始化”,第二个称为“直接列表初始化”。
概念是=
的表单分配了一个“复合值” - 一个由3个整数组成的值。并使用该值初始化x
。对于这样的初始化,应该只允许非explicit
构造函数。 x{1, 2, 3}
(没有等号)的概念是您使用 3 值初始化变量 - 概念上不是复合值,而是您碰巧同时提供的3个单独值。您可以说这是该术语最一般意义上的“构造函数调用”。
您展示的其他初始化实际上与上述两个完全不同:
T x({1, 2, 3});
它只调用T
的构造函数作为参数。{1, 2, 3}
。它没有做任何奇特的事情,比如如果T
是数组则初始化数组,或者如果T
是聚合结构/类,则初始化struct成员。如果T
不是类,则该声明无效。但是如果T
碰巧有副本或移动构造函数,那么它可以依次使用该构造函数通过复制列表初始化构造临时T
并将复制/移动构造函数引用参数绑定到该临时。我相信你不会经常在真实的代码中使用这种形式。
所有这些都记录在委员会提案文件中,用于初始化程序列表。在这种情况下,您想要查看Initializer Lists — Alternative Mechanism and Rationale,在“程序员的初始化类型视图”部分:
我们观察到那些了解复制之间差异的专家程序员 初始化和直接初始化经常错误地认为前者的效率低于后者。 (实际上,当两个初始化都有意义时,它们同样有效。)
相反,我们发现以不同的方式思考这些事情会更有用:
- 通过调用构造函数构建(“ctor-call”)
- 通过转移价值构建(“转换”)
(碰巧,前者对应于“直接初始化”,后者对应于“复制 - 初始化“,但标准的术语对程序员没有帮助。”
稍后,他们找到了
请注意,因为我们会在
中处理{ ... }
X x = { ... };
作为单个值,它不等于
X x{ ... };
其中
{ ... }
是构造函数调用的参数列表(我们强调它,因为它与N2531不同)。
C ++ 0x FDIS中规定的规则与该论文中提出的规则略有不同,但该论文中提出的基本原理在C ++ 0x FDIS中保留并实现。
答案 2 :(得分:4)
从理论的角度来看,给出的答案是很好的,但也许一些实际的例子也是有用的。通过统一初始化,现在可以编写之前根本不可能的构造。例如:
初始化成员数组。
全局常量容器(例如地图)。
即便:
class Foo
{
int data[3];
public:
Foo() : data{1,2,3} { }
};
这里我们可以直接初始化成员数组而无需赋值(考虑默认构造不可用的情况)。
const std::map<int, std::string> labels {
{ 1 , "Open" },
{ 2 , "Close" },
{ 3 , "Reboot" } };
有时,只读全局查找对象很有用,但如果没有统一初始化,则无法用数据填充它。
答案 3 :(得分:1)
我认为语法一致性在泛型编程中非常重要。例如,考虑一下,
#include <utility>
#include <tuple>
#include <vector>
template <class T>
struct Uniform {
T t;
Uniform() : t{10, 12} {}
};
int main(void)
{
Uniform<std::pair<int, int>> p;
Uniform<std::tuple<int, int>> t;
Uniform<int [2]> a;
Uniform<std::vector<int>> v; // Uses initializer_list
return 0;
}