23 Aug 2017
C++11特性介绍——运行时常量、lambda
编译时常量和运行时常量
- 运行时常量: const 用来修饰函数参数,函数返回值,函数本身,类等等,const描述都是一些“运行时常量性”的概念。
但是, 我们很多时候需要的是编译期常量, 如下例子:
const int GetConst()
{
return 1;
}
int arrs[GetConst] = { 0 }; //明显无法通过编译
switch (type)
{
case GetConst: //无法通过编译
break;
default:
break;
}
问题:
那么, 怎么获取编译时期的常量呢, 或者说定义编译时期常量?
方法1:
使用宏定义, 当然这是一种非常粗暴的方式, effective C++也建议一般不要使用宏定义
#define GETCONST 1
方法2:
编译时常量: C++11中对编译时期常量的回答是constexpr;
eg:
constexpr int GetConst()
{
return 1;
}
当然, C++11中, 常量表达式实际上可以作用的实体不仅仅限于函数, 还可以作用于数据声明, 以及类的构造函数等;
常量表达式函数
在函数返回类型前加入关键字constexpr使其成为常量表达式函数。并不是所有的函数都有资格成为常量表达式函数,需要符合以下几点:
- 函数体只有单一的return返回语句(里面有typedef, assert, static_assert, using等语句也不影响);
- 函数必须有返回值(不能是void);
- 在使用前必须已有定义(区别其他类型, 其他类型只要有声明即可, 这里是
定义
); - return 返回语句表达式中不能使用非常量表达式的函数,全局数据,且必须是一个常量表达式;
/*
条件1说明
*/
//编译肯定不会通过的
constexpr int data()
{
const int i = 1;
return i;
}
/*
条件3说明
*/
constexpr int f();
int a = f(); //编译通过
const int b = f(); //编译通过
//无法通过编译, c是编译时常量, 肯定要求编译期计算值, 然而f()此时还没有定义;
constexpr int c = f();
constexpr int f()
{
return 1;
}
//编译通过
constexpr int d = f();
/*
条件4 说明
*/
const int func() { return 1; };
//编译不通过, 很明显, return的表达式在运行时才能正确返回, 这和constexpr编译常量矛盾
constexpr int g() {return e(); };
常量表达式值
常量表达式值必须被一个常量表达式赋值, 而且表达式值在使用前必须被初始化;
const int i = 1;
constexpr int j = 2;
事实上, 两者在大数情况下是一样的, 不过有一点是肯定的, 就是如果i在全局名字空间中, 编译器一定会为i产生数据, 而对于j, 如果不是有代码明显地使用它的地址, 编译器可以选择不为他产生数据;
在C++11中, constexpr关键字不能用于修饰自定义类型的定义;
//这些都无法通过编译
constexpr struct MyType
{
int i;
}
constexpr MyType mt = {0}
struct MyType
{
constexpr MyType(int x): i(x)
{
}
int i;
}
constexpr MyType mt = { 0 };
constexpr修饰构造函数
- 函数体必须为空;
- 初始化列表只能由常量表达式来赋值;
C++11中, 不允许常量表达式作用于virtual的成员函数, 这个原因也是显而易见的, virtual表示的是运行时的行为, 与”可以在编译时进行值计算”的constexpr的意义是冲突的;
constexpr修饰函数模板
由于模板的不确定性, 所以模板函数是否被实例化为一个编译时常量版本也是未知的, 所以C++ 11规定, 如果在某个实例化之后, 模板函数实例化结果不满足编译常量需求, constexpr
lambda
lambda函数语法定义
[capture](parameters) mutable ->return-type{statement}
[capture]
: 捕获列表,捕获上下文中变量已提供给lambda函数使用;捕获列表总是出现在lambda的最开始出,是lambda的引出符;(parameters)
: 参数列表;与普通函数的参数列表一致,如果不需要参数传递可以连同()一起省去;mutable
: mutable修饰符。默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。该修饰符号下,参数列表不可以省去。->return-type
: 返回类型,无需返回值的时候,可以连同符号->一起删除,另外,在返回类型明确的情况下,也可省略让编译器对返回类型进行推导;{statement}
:函数体,内容和普通函数一样,内部可以使用参数,也可以使用所有捕获的变量;
捕获列表形式
- [var] 值传递捕获变量var;
- [=] 值传递捕获所有父作用域的变量(包括this);
- [&var] 引用传递捕获变量var;
- [&] 引用传递捕获所有父作用域的变量(包括this);
- [this] 值传递捕获当前的this指针;
- [=, &a, &b] 引用传递捕获a,b,其他使用值传递;
- [&, a, this] 值传递捕获a和this,引用捕获其他;
//lambda
int girls = 2;
int boys = 3;
auto totalChild = [](int x, int y) -> int{ return x + y; };
cout << totalChild(girls, boys) << endl;
//最简单的lambda函数
[]{};
auto totalChild1 = [girls, &boys]() ->int{return girls + boys; };
cout << totalChild1() << endl;
注:仿函数是编译器实现lambda的一种方式;
cuipf
