C++11:提高性能及操作硬件的能力(一)

2014-11-24 07:18:52 · 作者: · 浏览: 0

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 是一个元组。模板参数包如下:

template
  
   
class tuple;
tuple<1,2,3> one;//模板实例化
tuple
   
     twol
   
  

Elements就是一个模板参数包,有了这个模板包tuple模板类可以接受任意多个参数实例化模板,其本质就是将多个模板参数打包成Elements然后通过解包方式释放模板参数。

函数参数包如下:

template
  
   
void f(T...args);
  
其中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的应用实例:

#include 
  
   
#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; }
      
     
    
   
  
memory_order_release/consume的应用实例:

#include 
  
   
#include 
   
     #include 
    
      #include 
     
       using namespace std; atomic
      
        ptr; atomic
       
         data; void Prod