参考:Initializer lists and uniform initialization
initializer list
C++ 03 已经部分支持initializer lists,允许你对一些简单的聚合类型(aggregate data types)例如C风格的数组
struct Employee { int nID; int nAge; float fWage; }; Employee sJoe = {1, 42, 60000.0f}; int anArray[5] = { 3, 2, 7, 5, 8 };
但是对于class这种类型却不可以使用initializer list,因为类必须经由函数调用的方式,使用构造函数来初始化。这就导致intializer llists在使用上的不一致
int anArray[5] = { 3, 2, 7, 5, 8 }; // ok std::vector<int> vArray= {3, 2, 7, 5, 8}; // not okay in C++03
C++ 11 扩展了initializer lists的功能,现在initializer list适用于所有类型。其原理是通过std::initializer_list类实现的(在<initializ_list>中定义)。如果你是用initializer list,C++编译器会试图寻找接收std::initializer_list参数的ctor。而std::initializer_list本身的实现是通过关联array<T,n>来实现的。
std::vector<int> vArray = {3, 2, 7, 5, 8}; // calls constructor std::vector<int>(std::initializer_list<int>);
C++ 11 STL中几乎所有类型都有std::initializer_list类的ctor实现(complex类没有),所以你可以放心大胆的使用initializer lists。
你也可以在自己的类中实现 initializer_list constructors ,让后使用迭代器(iterator)来遍历initializer llist。
#include <vector> #include <initializer_list> using namespace std; template <typename T> class MyArray { private: vector<T> m_Array; public: MyArray() { } MyArray(const initializer_list<T>& il) { // Manually populate the elements of the array from initializer_list x for (auto x: il) // use range-based for statement to iterate over the elements of the initializer list m_Array.push_back(x); // push them into the array manually } }; int main() { MyArray<int> foo = { 3, 4, 6, 9 }; return 0; }
由于std::vector有initializer_list constructor,所以你可以直接使用vector自己的initializer_list constructor
MyArray(const std::initializer_list<T>& x): m_Array(x) // let vector constructor handle population of mArray
{}
由于initializer_list自己实现了begin(),end(),因此可以使用range-based for迭代initializer_list。
initializer_list还可以作为函数参数
int sum(const initializer_list<int> &il) { int nSum = 0; for (auto x: il) // use range-based for statement to iterate over the elements of the initializer list nSum += x; return nsum; } cout << sum( { 3, 4, 6, 9 } );
完整代码
#include<iostream> #include<vector> #include <complex> // std::complex #include<initializer_list> using namespace std; template <typename T> class MyArray { private: vector<T> m_Array; public: MyArray() { } MyArray(const initializer_list<T>& il) { // Manually populate the elements of the array from initializer_list x for (auto x : il) // use range-based for statement to iterate over the elements of the initializer list m_Array.push_back(x); // push them into the array manually } }; template<typename T> T sum(const initializer_list<T>& il) { T nSum = 0; for (auto x: il) { nSum += x; } return nSum; } template <typename T> void useMyArray(const MyArray<T>& x) { } template <typename T> MyArray<T> makeMyArray(void) { return MyArray<float>({ 2, 3.5f }); // use uniform initialization to create a MyStruct implicitly } int main() { vector<int> foo{ 1,2,3,4 };//等价于vector<int> foo({1,2,3,4}) vector<int> foo1 = { 1,2,3,4 }; vector<int> foo2(3, 2); vector<int> foo3[1]{ {1,2,3} }; vector<int> foo4[1] = { {1,2,3} }; vector<int> foo5(8);// creates an empty vector of size 8, using the int constructor vector<int> foo6{ 8 };// creates a one-element vector with data value 8, using the initializer_list constructor vector<int> foo7[8]; complex<double> foo8 = { 2.0,2.0 }; complex<double> foo9 = { 2.0,2.0 }; useMyArray<float>({ 2, 3.5f }); // use uniform initialization to create a MyStruct implicitly //MyArray<float> foo8 = makeMyArray(); cout << sum<float>({ 1,2.1,3,2,4.4 }) << endl; return 0; }
uniform initialization
type variable = { data, elements }; type variable { data, elements }; // note: no assignment operator
前者initializer list,后者uniform initialization。
为啥引入uniform initialization,主要是为了解决初始化的混乱。以前初始化可能因为(){} =的出现而发生,现在统一使用{}。
额外提一下,type variable = { data, elements }; 这种语句很容易让人误以为是operetor=,但实际上不是,他背后调的是ctor。想想ctor和operator=的区别,啥叫=? 只有人家实现存在在能是=,否则就只能是ctor。
narrowing
初值列(initializer list)会强迫造成所谓 value initialization,意思是即使某个local变量属于某种基础类型(那通常会有不明确的初值)也会被初始化为0(或nullptr一如果它是个pointer的话)
int i;//i has undefined value int j{};//j is initialized by 0 int* p;//p has undefined value int* q{};//q is initialized by nullptr
窄化(narrowing)——也就是精度降低或造成数值变动—对大括号而言是不可成立的
如何判断窄化是否合法,其依据是:current value能否被target type精确表述。
int x1(5.3);//OK,but OUCH:x1 becomes 5 int x2 = 5.3;//OK,but OUCH:x2 becomes 5 int x3{ 5.0 };//ERROR:narrowing int x4 = { 5.0 };//ERROR:narrowing char c1{5};//OK,even though 5 is an int,this is not narrowing char c2{ 9999 };//ERROR:narrowing std::vector<int> v1{ 1,2,3,4,5 };//OK std::vector<int> v2{ 1,3.14,6.28 };//ERROR: narrowing doubles to ints
Initializer lists vs initialization lists
initerlizer lists真是个糟糕的名字,因为他太容易与initialization lists混淆了。
An initialization list is used to do implicit assignments to class variables as part of a constructor:
MyStruct(int x, float y): m_nX(x), m_nY(y) {}; // m_nX and m_nY are part of the initialization list
An initializer list is a list of initializers inside brackets ( { } ) that can be used to initialize simple aggregate data types and classes that implement std::initializer_list:
std::vector<int> vArray = {3, 2, 7, 5, 8}; // vArray initialized using an initializer_list