据我所知,有三种方法可以用C ++初始化变量。
int x = 0; // C-like initialization
int x (0); // Constructor initialization
int x {0}; // Uniform initialization
为C++11启用了统一初始化,以便为初始化不同类型的变量提供更统一的语法,这需要C++03中的不同语法。
C-like,构造函数和统一初始化之间有什么区别?我应该总是使用统一初始化吗?
答案 0 :(得分:53)
首先,我建议查看Herb Sutter的following talk,其中他提供了有关该主题的一些建议。大括号初始化讨论从around 23:00开始。
当您谈论原始数据类型时,所有3个都会产生相同的结果。我个人更喜欢坚持使用旧的int x = 0
语法,但这取决于个人偏好。
对于类类型,大括号初始化和旧式构造函数初始化不是完全可互换的。例如:
vector<int> v (100); // Creates a 100-element vector
vector<int> v {100}; // Creates a 1-element vector, holding the value 100.
这是因为std::vector
有一个构造函数,它明确地将std::initializer_list
定义为唯一的参数。请记住
auto var = {1, 2};
创建std::initializer_list
,其中var
为其标识符。
关于初始化程序列表的事情是它们提供的一致性是可预期的可用变化。例如,如果要在C ++中初始化数组,则使用:
int arr[] = {1, 2, 3, 4};
但是,如果您想要使用相同的元素初始化vector<int>
,则必须:
arr
和arr + 4
使用C ++ 11,您可以使用
vector<int> v = {1, 2, 3, 4}; // Same syntax. Nice! Note that the = is optional
大括号初始化有用的另一个例子是它为C ++的most vexing parse提供了一种解决方法。从谈话中,假设我们有两个类origin
和extents
,可以传递其实例以构造另一个类型为rectangle
的对象。以下声明:
rectangle w(origin(), extents());
不允许您使用rectangle
和origin
临时对象创建extents
对象,因为该语句被解析为函数声明。 Tsk tsk。通常情况下,您必须这样做:
origin o;
extents e;
rectangle w(o, e);
使用大括号初始化,您可以动态创建它们,
rectangle w {origin(), extents()};
将按预期工作,即传递给构造函数,该构造函数重载了origin
对象作为它的第一个参数,而extents
对象作为第二个参数。
规则适用于对象,除非您有理由不使用大括号,否则请使用大括号初始化。
答案 1 :(得分:14)
c-like,构造函数和统一初始化之间有什么区别?
对于像int
这样的原始类型,没有实际的区别;所以让我们考虑一下班级类型T
。
第一种风格相当于
T x(T(0));
从初始化表达式创建临时对象,然后通过移动或复制它来初始化x
。在实践中,移动或复制将被省略,因此结果与第二种风格相同;唯一的区别是,如果没有可访问的副本或移动构造函数,第一个将失败。
第二个使用带有一个参数的构造函数直接初始化对象,如果没有合适的构造函数则给出错误。
第三个取决于可用的构造函数。
std::initializer_list
,它会使用它; 我应该总是使用统一初始化吗?
没有。有时您需要函数式初始化来区分initializer_list
构造函数和采用其他参数类型的构造函数。例如:
std::vector<int> v1(10, 42); // 10 elements with value 42
std::vector<int> v2{10, 42}; // 2 elements with values 10 and 42
你也不应该称它为“统一初始化”,因为它在任何有意义的意义上都不是“统一的”。官方术语是“支撑初始化”。