C++代码规范与优化——初始化列表&Range-Base for
2016-05-06
C++代码规范与优化——初始化列表&Range-Base for
c++11为了使代码更为规范和简洁,增加了一些新的特性。今天在这里讲初始化列表和Range-Base for两点。
一致性初始化
先看一个例子。
complex *p = new complex(0.1, 0.2);
int a[] = {1, 2, 3};
int b = 3;
多样式的初始化方式使得程序猿在编程时的选择会比较混乱,c++11标准为此引入了**一致性初始化(uniform initialization)**的概念,使得对所有的初始化行为都采用相同的语法。
complex *p = new complex {0.1, 0.2};
int a[] {1, 2, 3};
int b {3};
一般来说,一致性初始化主要有以下几种语法:
T object {arg...}; //变量初始化,可嵌套
T {arg...}; //临时变量
new T {arg...}; //堆变量
return {arg...}; //返回值
function ({arg...}); //实参
object[{arg...}]; //运算符参数
T({arg...}); //构造参数
Class {T member = {arg...};} //成员初始化
Class::Class() : member{arg...} {} //初始化列表
T object = {arg...}; //赋值构造
需要注意的是,初始化列表不支持某些隐式转换,称之为窄化(narrowing),适用以下几个规则:
- 不支持从浮点型转为整型
- 不支持从
long double
到double
或float
的转换,不支持从double
到float
的转换,除非参数是一个返回值在目标范围内的常量表达式 - 对于整型到浮点型的转换,满足参数是一个返回值在目标范围内的常量表达式
- 整数或无作用域的枚举类型转换为整型可能损失精度,除非参数是一个返回值在目标范围内的常量表达式
另外,初始化表达式不是一个表达式,它没有类型,即不能够decltype({1})
。正因为如此,在模板类型中它不能做类型推导,如template <class T> void f(T);
不能通过表达式f({1, 2, 3})
来进行调用。当我们用auto
来对初始化列表进行推导时,编译器将对其以std::initializer_list
对待。
一个嵌套的例子:
std::map<int, std::string> m = { // nested list-initialization
{1, "a"},
{2, {'a', 'b', 'c'} },
{3, s1}
};
Range-Base for
基于范围的for
循环操作, 比传统的for
语法更具可读性,可以迭代某个区间、数组或者集合内的每一个元素,在其它语言中称为foreach。语法如下:
for(range_declaration : range_expression ) {
loop_statement
}
在执行时,range_declaration会是遍历range_expression中解引用的结果,和传统的循环一样,作为临时变量时,range_declaration的生命周期为循环结束,但另一方面,range_declaration可以通过引用方式来实现,可以节约具有复杂构造函数类型的local copy。另外,基于范围的for
依然可以通过break或者continue进行相应操作。
int a[] {1, 2, 3, 4, 5, 6, 7, 8, 9};
std::cout << "initializer list range-base for :" << std::endl;
for (auto i : {0, 1, 2, 3, 4, 5}) {
std::cout << "a[" << i << "] = " << a[i] << ",";
}
std::cout << "\narray range-base for :" << std::endl;
for (auto element : a) {
std::cout << "a[] = " << a[i] << ",";
}
实际上,使用Range-Base for要求满足以下两点:
- 有成员函数
begin()
和end()
函数返回迭代器,或者满足全局函数std::begin()
和std::end()
- 迭代器有操作符运算
*,++,!=
数组类型就是满足全局的std::begin()
和std::end()
操作,返回的迭代器为指针,具有*,++,!=
操作。下面这个例子实现了自定义链表结构的遍历操作:
template <class T>
struct LinkNode {
T data;
LinkNode *next;
};
template <class T>
struct LinkIter {
LinkNode<T> *ptr;
LinkNode<T> *&operator *() {
return ptr;
}
LinkIter<T> &operator ++() {
this->ptr = this->ptr->next;
return *this;
}
bool operator !=(LinkIter<T> &var) {
return ptr != var.ptr;
}
};
template <class T>
class LinkList {
public:
LinkList() : head(nullptr), tail(nullptr) {}
LinkIter<T> begin() {
return LinkIter<T>{head};
}
LinkIter<T> end() {
return LinkIter<T>{nullptr};
}
void add(T p) {
auto node = new LinkNode<T>{ p, nullptr };
if (!head) {
head = node;
tail = node;
}
else if (!head->next) {
head->next = node;
tail = node;
}
else {
tail->next = node;
tail = node;
}
}
private:
LinkNode<T> *head, *tail;
};
int main() {
LinkList<int> ll;
for (auto i :{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }) {
ll.add(i);
}
for (auto ld : ll) {
std::cout << ld->data << " ";
}
return 0;
}
以上就是关于初始化列表和基于范围的for
的简单说明,c++的其它特性在之后的文章中介绍。