initialization list,initializer list,uniform initialization的区别

时间:2021-09-04 22:46:17

参考: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