C++
大约 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的重载版本,如vector
、min
和max