跳至主要內容

C++

LincZero大约 3 分钟

C++

目录

一致性初始化/列表初始化 Uniform Initialization/Initializer Lists

基本用法

旧写法

Before C++11, programmers, especially novices, could easily become confused by the question ofhow to initialize a variable or an object. Initialization could happen with parentheses, braces, and/orassignment operators.

即以前的代码初始化写法很多

Rect r1 = {3, 7, 20, 25, &area, &print};	// 等号、大括号
Rect r2 (3, 7, 20, 25);						// 小括号
int ia[6] = {27, 210, 21, 47, 109, 83};		// 中括号、等号、大括号

新写法

现在所有的初始化语法可以只用大括号完成(声明变量后跟一个大括号)

int values[] {1,2,3};
vector<int> v {2,3,5,7,11,13,17};
vector<string> cities {
	"Berlin", "New York", "London", "Braunschweig"
};
complex<double> c{4.0,3.0};	// equivalent to c(4.0, 3.0)

底层原理

其实是利用一个事实:编译器看到{t1,t2... tn}便做出一个initializer_list<T>,它关联至一个array<T, n>(n是个数)。

有两种情况:

  • 调用函数(例如ctor)时,函数一次只能接受一个参数,该array内的元素可被编译器分解逐一傅给函数
  • 但若函数参数是个initializer_list<T>, 调用者却不能给予数个T参数然后以为他们会被自动转为一个initializer_list<T>传入

(补充:其中ctor是包含类的构造函数,ctor == constructor,dtor == destructor)

结合代码谈原理1

vector<string> cities { "Berlin", "New York", "London" };

// 形成一个 initializer_list<string>,背后有个 array<string, 6>
// 调用 vector<string> ctors 时,
// 编译器找到一个 vector<stirng> ctor 接受 initializer_list<string>

结合代码谈原理2

complex<double> c {4.0, 3.0};

// 形成一个 initializer_list<double>,背后有个 array<double, 2>
// 调用 complex<double> ctor 时,
// 该array内的2个元素被分解传给ctor,complex<double>并无任何ctor接受initializer?

其他特性

初始化缺省

一致性初始化缺省时可以自动设置初值

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

不允许隐式转换窄缩

不允许隐式转换窄缩

int x1 (5.3);		// OK, but OUCH: xl beco
int x2= 5.3;		// OK, but OUCH: x2 beco.
int x3{5.0};		// ERROR: narrowing
int x4 = {5.3};		// ERROR: narrowing
char c1{7};			// OK: even though 7 is an int, this is not narrowing
char c2{99999};		// ERROR: narrowing (if 99999 doesn't fit into a char)
std::vector<int> v1 { 1,2,4,5 };		// OK
std::vector<int> v2 { 1,2.3,4, 5.6 };	// ERROR: narrowing

initializer_list<T>类型

作参数参

void print(std::initializer_list<int> vals)
{
    for(auto p = vals.begin(); p!=vals.end(); ++p){
        std::cout << *p << endl;
    }
}
print({12,3,5,7,11,13,17});	// pass a list of values to print()

作构造函数参(优先级问题)

若函数同时有接收多个参数的重载版本和接收initializer list的重载版本,则优先调用接收initializer list的重载版本

重载优先级

class P
{
public:
    P(int a, int b)
    {
        cout << "P(int, int), a=" << a << ",b=" << b << end;
    }
    P(initializer_list<int> initlist)
    {
        cout << "P(initializer_list<int>), vlaue= ";
        for(auto i:initlist)
            cout << i << " ";
        cout << endl;
    }
};

P p(77,5);			// P(int, int), a=77,b=5
P q{77,5};			// P(initializer_list<int>), vlaue= 77 5
p r{77,5,42};		// P(initializer_list<int>), vlaue= 77 5 42
P s={77,5};			// P(initializer_list<int>), vlaue= 77 5

底层原理

initializer_list<T>源代码

template<class _E>
class initializer_list{
public:
	typedef _E			value_type;
	typedef const _E&	reference;
	typedef const _E&	const_reference;
	typedef size_t		size_type;
	typedef const _E*	iterator;
	typedef const _E*	const_iterator ;

private:
	iterator			_M__array;			// array迭代器头部(这里是指针,拷贝时只是浅拷贝)
	size_type			_M_len;				// 长度
    
	// The compiter can calt a private constructor. 编译器可以调用 私有构造函数。
	constexpr initializer_list(const_iterator _a, size_type __l)
        :_M_array(__a), _M_len(__l) { }		// 构造时会构造一个array

public:
	constexpr initializer_list( ) noexcept
        :_M_array(0), _M_len(0) { }

    // Number of etements.
	constexpr size_type
	size() const noexcept { return _M_len; }
    
	// first etement.
	constexpr const_iterator
	begin() const noexcept { return _M_array; }

    // 0ne past the iast eLement.
	constexpr const iterator
    end() const noexcept { return begin() -> ???; }
};

STL中的使用

STL中的大部分容器和算法相关函数均有接收initializer list的重载版本,如vectorminmax