1 constexpr编译时期常量
constexpr用于函数:
constexpr int get(){return 10};
int array[get()];//get()返回一个编译期常量可以用于声明数组大小
constexpr int a=get();//a是一个编译期常量
int b=get();//此时get当一个普通函数使用,b不再是常量常量表达式函数必须满足:
1) 函数体只能有一句return
constexpr int get(){
int a=10;//int a=10不可以,但是像static_assert这样编译时确定的还是可以的,assert是运行时断言也不可以
return 10;
}2) 函数必须有返回值,所以constexpr void get();是不可以的
3) 使用前必须定义,即在用函数初始化一个常量时必须先定义
4) return不能返回非常量的表达式的函数、全局数据,如:constexpr int get(){ return fun();}返回若fun不是constexpr函数是不可以的
C++11规定浮点数常量表达式是不允许的,因为浮点环境可能改变
常量表达式值只能被常量表达式赋值,即:constexpr int a=1;是可以的,但是constexpr int a=i;是错误的。
C++11中constexpr是不能修饰自定义类型的,但是可以定义常量构造函数:
#include常量表达式用于模板函数using namespace std; struct Date { constexpr Date(int y, int m, int d):year(y), month(m), day(d) {}//常量构造函数 constexpr int GetYear() { return year; } constexpr int GetMonth() { return month; } constexpr int GetDay() { return day; } private: int year; int month; int day; }; constexpr Date PRCfound {1949, 10, 1};//常量类型由常量构造函数 Date PRCfound(1949,10,1)//调用constexpr Date等于调用普通构造函数,也就是说此时constexpr忽略 constexpr int foundmonth = PRCfound.GetMonth();//成员函数也需要constexpr int Getmonth声明为constexpr int main() { cout << foundmonth << endl; } // 10
struct NotLiteral{
NotLiteral(){ i = 5; }
int i;
};
template
constexpr T ConstExp(T t) {//该模板函数用于返回一个constexpr型的NotLiteral
return t;
}
void g() {
NotLiteral nl;
NotLiteral nl1 = ConstExp(nl);//此时当普通函数使用
constexpr NotLiteral nl2 = ConstExp(nl); // 无法通过编译,nl不是常量表达式
constexpr int a = ConstExp(1);
}
2 变长模板
一个变长的宏__VA_ARGS__可以输出变长参数。C++11中提出了变长模板参数包和函数参数包。c++11中std::tuple就是个变长参数模板类,它是pair类型的泛化,可以指定多个元组,如:tuple
templateclass tuple; tuple<1,2,3> one;//模板实例化 tuple twol
Elements就是一个模板参数包,有了这个模板包tuple模板类可以接受任意多个参数实例化模板,其本质就是将多个模板参数打包成Elements然后通过解包方式释放模板参数。
函数参数包如下:
template其中T是模板参数包,args是函数参数包。void f(T...args);
3 原子类型
通常线程间同步通信都会想到mutex,condition_variable这样的大杀器,但是通常一些简单的线程同步可以利用原子类型来进行。原子操作:要么不做,要么一步完成。关于原子类型及其操作见:点击打开链接,原子操作对于lock free编程大有好处。
这里讲一下内存序memory order,编译器会对源代码做一些优化,使得一些语句顺序优化后可引起线程间产生错误,比如:
#include#include #include using namespace std; atomic a; atomic b; int Thread1(int) { int t = 1; a = t; b = 2; } int Thread2(int) { while(b != 2) ; // 自旋等待 cout << a << endl; // 总是期待a的值为1,但是由于编译器优化,可能使得b=2在a=t之前执行 } int main() { thread t1(Thread1, 0); thread t2(Thread2, 0); t1.join(); t2.join(); return 0; }
在C++11前linux内核采用了一个叫做内存栅memory barrier来强制汇编代码执行顺序。C++11允许供程序员使处理器以指定的顺序执行机器指令的机制memory order。memory order是个枚举类型,专门针对原子类型的,因为一般变量不用于线程间同步没必要指定顺序,其枚举值如下:
| 枚举值 | 语义 |
| memory_order_relaxed | 不对指令执行顺序做任何保证 |
| memory_order_acquire | 本线程中,所有后序的读操作必须在本条原子操作完成后进行 |
| memory_order_release | 本线程中,所有之前的写操作完成后才能执行本条原子操作 |
| memory_order_acq_rel | 同时包含memory_order_acquire和memory_order_release语义 |
| memory_order_consume | 本线程中,所有后序的有关本原子类型的操作,必须在本条原子操作完成之后执行,注意是本原子类型关联的后序操作,是memory_order_acquire的弱化 |
| memory_order_seq_cst | 全部存取指令按照顺序执行,最强顺序,称为顺序一致性,是C++11中所有atomic原子操作的默认值 |
memory_order_acquire/release的应用实例:
#includememory_order_release/consume的应用实例:#include #include using namespace std; atomic a; atomic b; int Thread1(int) { int t = 1; a.store(t, memory_order_relaxed); b.store(2, memory_order_release); // 本原子操作前所有的写原子操作必须完成 } int Thread2(int) { while(b.load(memory_order_acquire) != 2); // 本原子操作必须完成才能执行之后所有的读原子操作 cout << a.load(memory_order_relaxed) << endl; // 1 } int main() { thread t1(Thread1, 0); thread t2(Thread2, 0); t1.join(); t2.join(); return 0; }
#include#include #include #include using namespace std; atomic ptr; atomic data; void Prod