在 C++98 中,支持了在类声明中使用等号 "=" 加初始值的方式,来初始化类中静态成员常量
。这种声明方式也称之为"就地"声明。
在 C++11 中,增加了对普通成员、静态成员进行就地声明的支持,还允许以 {}
的方式进行就地声明。同时支持初始化列表。
程序员可以为同一变量既声明就地的列表初始化,又在初始化列表中进行初始化,只不过初始化列表总是看起来"后作用于"非静态成员。也就是说,初始化列表的效果总是优先于就地初始化的。
C++ 中对变量的初始化方式有多种,为了统一与安全,C++11 中引入了一致性初始化。
一致性初始化示例:
int i{};
int *q{};
double x1{5.3};
int values[]{1, 2, 3};
std::vector<int> v{2, 3, 5, 7, 11, 13, 17};
一致性初始化使赋值的类型更加安全:
int x = 5.3; // 编译成功,类型截断
int x{5.3}; // 编译失败, 5.3 是实数
C++11 的一致性初始化是通过 std::initializer_list
来实现的。当检测到 {}
初始化变量时,编译器会将 {} 里面的参数装入一个 std::initializer_list 列表。
template<typename _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;
size_type _M_len;
// 提供一个私有构造函数供编译器调用,用于构造初始化列表
constexpr initializer_list(const_iterator __a, size_type __l) : _M_array(__a), _M_len(__l) {}
public:
constexpr initializer_list() noexcept : _M_array(0), _M_len(0) {}
constexpr size_type size() const noexcept { return _M_len; }
constexpr const_iterator begin() const noexcept { return _M_array; }
constexpr const_iterator end() const noexcept { return begin() + _M_len; }
};
从上面的设计可以看出:
- std::initializer_list 可以接收任意长度的初始化列表,但要求元素必须是同种类型 T (或可转换为 T)。因为 std::initializer_list 是由一个数组构造的。
- std::initializer_list 提供了 3 个成员接口: size()、begin()、end(),而且这些接口都是只读的。因此,无法修改其中某个元素的值,但可以通过初始化列表的赋值对其作整体修改。
- std::initializer_list 是高效的。它的内部并不负责保存初始化列表中元素的拷贝,仅仅存储了列表元素的引用而已。也因此要避免如下错误的使用方法:
/** 错误用法 */
std::initializer_list<int> func(void)
{
int a = 1, b = 2;
return { a, b };
}
/** 正确用法 */
std::vector<int> func(void)
{
int a = 1, b = 2;
return { a, b };
}
C++11 中, 标准模板类支持列表初始化,其使用 std::initializer_list
这个轻量级的类模板来完成该功能。
自定义类列表初始化:
class Foo
{
public:
Foo(std::initializer_list<int>) {}
};
Foo foo = { 1, 2, 3, 4, 5 }; // ok
自定义容器列表初始化:
class FooVector
{
std::vector<int> content_;
public:
FooVector(std::initializer_list<int> list)
{
for (auto it = list.begin(); it != list.end(); it++)
{
content_.push_back(*it);
}
}
};
class FooMap
{
std::map<int, int> content_;
using pair_t = std::map<int, int>::value_type;
public:
FooMap(std::initializer_list<pair_t> list)
{
for (auto it = list.begin(); it != list.end(); it++)
{
content_.insert(*it);
}
}
};
FooVector foo_1 = { 1, 2, 3, 4, 5 };
FooMap foo_2 = { {1, 2}, {3, 4}, {5, 6} };
作为同类型参数列表传递:
void func(std::initializer_list<int> l)
{
for (auto it = l.begin(); it != l.end(); it++)
{
std::cout << *it << std::endl;
}
}
// main
func({}); // 空集合
func({1, 2, 3});