C++11新特性

 变参宏定义

#define LOG(...) {\
    fprintf(stderr, "%s: Line %d: \t", __FILE__, __LINE__); \
    fprintf(stderr, __VA_ARGS__); \
    fprintf(stderr, "\n"); \
}

int main() {
   int x = 3;
   LOG("x = %d", x);
   return 0;
}

 C++11中,非常量的静态成员变量需要到文件以外定义。这样做可以保证在编译时,类静态成员的定义最后只存在于一个目标文件中。

外部模板声明不能用于一个静态函数,因为静态函数没有外部链接属性,不可能在本编译单元之外出现,但是可以用于类静态成员函数。

 在代码清单2-30中,我们定义了一个模板类X和一个模板函数TempFun,然后分别用普通的全局结构体、匿名的全局结构体,以及局部的结构体作为参数传给模板。可以看到,

使用了局部的结构体C及变量c,以及匿名的结构体B及变量b的模板类和模板函数,在C++98标准下都无法通过编译。而除了匿名的结构体之外,匿名的联合体以及枚举类型,在C++98标准下也都是无法做模板的实参的。如今看来这都是不必要的限制。所以在C++11中标准允许了以上类型做模板参数的做法﹐故而用支持C++11标准的编译器编译以上代码,代码清单2-30所示代码可以通过编译。

委派构造函数只能在函数体中为成员变量赋值,不能使用初始化列表。

struct Rule1 {
    int i;
    Rule1(int a) : i(a) {}
    Rule1() : Rule1(40), i(i) {}  //这里委派了Rule1(40),但是其使用了初始化列表,不能通过编译
};

但是如果私有化委派构造函数,可以通过编译,改进方法如下:

class Info {
public:
    Info() : Info(1, 'a') {}
    Info(int i) : Info(i, 'a') {}
    Info(char e) : Info(1, e) {}
private:
    Info(int i, char e) : type(i), name(e) {/*其他初始化*/}
    int type;
    char name;
    //....
};  

使用构造模板函数

#include <list>
#include <vector>
#include <queue>
#include <iostream>
using namespace std;

class TDConstructed {
private:
    template <class T> TDConstructed(T first, T last) 
        : li(first, last) {} 
    list<int> li;
public:
    TDConstructed(vector<short> &v) :
        TDConstructed(v.begin(), v.end()) {}
    TDConstructed(deque<int> &d) :
        TDConstructed(d.begin(), d.end()) {}
};

目标函数产生的异常,如果委派函数使用了try语句,则可以捕获异常。

class DCExcept {
public:
    DCExcept(double d)
        try : DCExcept(1, d) {
            cout << "Run the  body." << endl;
            //其他初始化
        } catch (...) {
            cout << "caught exception." << endl;
        }
private:
    DCExcept(int i, double d) {
        cout << "going to throw!" << endl;
        throw 0;
    }
    int type;
    double data;
};

在代码清单3-15中,我们在目标构造函数DCExcept(int,double)抛出了一个异常,并在委派构造函数DCExcept(int)中进行捕捉。编译运行该程序,我们在实验机上获得以下输出:

右值引用:移动语义和完美转发

可以取地址的是左值,不可以取地址的是右值。

右值分为将亡值,纯右值,字面值是纯右值,类型转换函数的返回值,lambda表达式都是右值。

而将亡值则是C++11新增的跟右值引用相关的表达式,这样表达式通常是将要被移动的
对象(移为他用),比如返回右值引用T&&的函数返回值、std::move的返回值(稍后解释),
或者转换为T&&的类型转换函数的返回值(稍后解释)。而剩余的,可以标识函数、对象的
值都属于左值。在C++11的程序中,所有的值必属于左值、将亡值、纯右值三者之一。
 

T&& a = ReturnRvalue();

T b = ReturnRvalue();

上面的代码少一次对象的析构和构造,因为右值引用直接绑定了ReturnRvalue()返回的临时量,而b是由临时值构造而成的。

const 左值引用可以绑定右值上。

C++判断引用类型

标准库<type_traits>提供了三个模板类,is_rvalue_reference, is_lvalue_reference, is_reference

比如cout << is_rvalue_reference<string&&>::value;

判断类型是否可以移动

is_move_constructible, is_trivially_move_constructible, is_nothrow_move_constructible,使用方法时其成员value

cout << is_move_constructible<UnknownType>::value;

编译器优化RVO/NRVO,(Return Value Optimization返回值优化,NRVO,Named Return Value optimization),使用-fno-elide-constructors选项在g++/clang++关闭优化。

完美转发:指在函数模板中,完全依照模板的参数的类型,将参数传递给函数模板中调用的另外一个函数。

 C++引用折叠:

 

 

 

template <typename T>
void IamForwording(T && t) {
    IrunCodeActually(static_cast<T &&>(t));
}

 

 完美转发的一个作用就是做包装函数。

 

 initializer_list<int> 的使用

#include <iostream>
#include <vector>
using namespace std;

class Mydata {
public:
    Mydata & operator [] (initializer_list<int> l) {
        //用idx记录下标索引
        for(auto i = l.begin(); i != l.end(); i++) {
            idx.push_back(*i);
        }
        return *this;
    }
    Mydata& operator = (int v) {
        if(idx.empty() != true) {
            for(auto i = idx.begin(); i != idx.end(); i++) {
                d.resize((*i > d.size()) ? *i : d.size());
                //将指定下标的地方全部替换为v
                d[*i - 1] = v;
            }
            idx.clear();
        }
        return *this;
    }
    void Print() {
        for(auto i = d.begin(); i != d.end(); i++) {
            cout << *i << " ";
        cout << endl;
    }
private:
    vector<int> idx;
    vector<int> d;
}
int main() {
    Mydata d;
    d[{2, 3, 5}] = 7;
    d[{1, 4, 5, 8}] = 4;
    d.Print();  // 4 7 7 4 4 0 0 4
}

在中括号可以使用列表,指定需要修改的下标。

初始化列表可以用来防止类型收窄。

判断是否是标准布局:is_standard_layout<T>::value

判断是否为POD类型,std::is_pod

#include <string>
using namespace std;

union T {
    string s;  //string 有非平凡构造函数
    int n;
};

int main() {
    T t;  //构造失败,因为T的构造函数被删除了
}

在代码清单3-41中,联合体T拥有一个非POD的成员s。而string有非平凡的构造函
数,因此T的构造函数被删除,其类型的变量t也就无法声明成功。解决这个问题的办法是,
由程序员自己为非受限联合体定义构造函数。通常情况下,placement new会发挥很好的作用,
如代码清单3-42所示。

#include <string>
using namespace std;

union T {
    string s;
    int n;
public:
    //自定义构造函数和析构函数
    T () {new (&s) string; }  //这句话的意思是指定地址,进行new操作,new 一个空string
    ~T() { s.~string();}
};
int main() {
    T t; //构造成功
 }

采用了placement new 将s构造在其地址&s处。在这个例子中,析构的时候也必须是一个string对象,否则可能会出现问题。

#include <cstring>
using namespace std;

struct Student {
    Student(bool g, int a) :gender(g), age(a){}
    bool gender;
    int age;
}

class Singer {
public:
    enum Type {STUDENT, NATIVE, FOREIGNER};
    Singer(bool g, int a) :s(g, a) {t=STUDENT;}
    Singer(int i): id(i){ t = NATIVE; }
    Singer(const char* n, int s) {
        int size = (s > 9) ? 9 : s;
        memcpy(name, n, size);
        name[s] = '\0';
        t = FOREIGNER;
    }
    ~Singer() {}
private:
    Type t;
    union {  //匿名的非受限联合体
        Student s;
        int id;
        char name[10];
    };
};
int main() {
    Singer(true, 13);
    Singer(310217);
    Singer("J Michael", 9);
}

字符串后缀操作符

#include <cstdlib>
#include <iostream>
using namespace std;

typedef unsigned char uint8;
struct RGBA {
    uint8 r;
    uint8 g;
    uint8 b;
    uint8 a;
    RGBA(uint8 R, uint8 G, uint8 B, uint8 A = 0)
        :r(R), g(G),b(B),a(A) {}
};

RGBA operator "" _C (const char* col, size_t n) {
    const char* p = col
    const char* end = col + n;
    const char* r, *g, *b, *a;
    r = g = b = a = nullptr;
    for(; p != end; ++p) {
        if(*p == 'r') r = p;
        else if (*p == 'g') g = p;
        else if (*p == 'b') b = p;
        else if (*p == 'a') a = p;
    }
    if((r == nullptr ) || (g == nullptr ) || (b == nullptr))
        throw;
    else if ( a == nullptr)
        return RGBA(atoi(r+1), atoi(g+1), atoi(b+1));
    else
        return RGBA(atoi(r+1), atoi(g+1), atoi(b+1), atoi(a+1));
}

std::ostream& operator << (std::ostream * out, RGBA & col) {
    return cout << "r: " << (int)col.r
        << ", g: " << (int)col.g
        << ", b: " << (int)col.b
        << ", a: " << (int)col.a << endl;
}

void blend(RGBA && col1, RGBA && col2) {
    cout << "blend " << endl << col1 << endl << col2 <<endl;
}

int main() {
    blend("r255 g240 b155"_C, "r15 g255 b10 a7"_C);
}

量操作符( literal operator )函数: RGBA operator "" _C(const char* col, size_ t n)函数。这个函数会解析以C为后缀的字符串,并返回-一个RGBA的临时变量。

struct Watt{ unsigned int v; };

Watt operator "" _w(unsigned long long v) {
    return {(unsigned int)v};
}

int main() {
    Watt capacity = 1024_w;
}

使用_w表示瓦特,自定义字面量还可以用于浮点整数,但应该满足如下规则:

如果字面量位整型数,那么字面量的操作符函数只可以接受unsigned long long 或者const char* 为其参数,当unsigned long long 无法容纳字面量的时候,编译器会自动将该字面量转化为以'\0'结束的字符串,并调用以const char* 为参数的版本进行处理。

如果字面量为浮点整数,则字面量操作符函数只可接受longdouble或者constchar*为参数。const char*版本的调用规则同整型的一样(过长则使用const char*版本)。

如果字面量为字符串,则字面量操作符函数函数只可接受const char*, size_ t为参数(已知长度的字符串)。

如果字面量为字符,则字面量操作符函数只可接受一个char为参数。

)在字面量操作符函数的声明中,operator "与用户自定义后缀之间必须有空格。
后缀建议以下划线开始。不宜使用非下划线后缀的用户自定义字符串常量,否则会被编
译器警告。当然,这也很好理解,因为如果重用形如201203L这样的字面量,后缀“L”
无疑会引起一些混乱的状况。为了避免混乱,程序员最好只使用下划线开始的后缀名。

is_same判断类型是否一致。

using可以使用与模板别名

template <typename T> using MapString = std::map<T, char*>;

MapString<int> numberedString;

auto不能使用的地方

(1)函数中,不能用于形参类型。

(2)结构体中,非静态成员变量的类型不能是auto的。

(3)不能声明auto数组。

(4)实例化模板的时候,不能使用auto作为模板参数。

查看类的名称:typeid(a).name()

查看类的hash值:typeid(a).hash_code() == typeid(b).hash_code()

1)如果e是一个没有带括号的标记符表达式( id-expression )或者类成员访问表达式,
那么decltype(e)就是e所命名的实体的类型。此外,如果e是-一个被重载的函数,则会导致
编译时错误。
2)否则,假设e的类型是T,如果e是一个将亡值(xvalue), 那么decltype(e)为T&&。
3)否则,假设e的类型是T,如果e是一个左值,则decltype(e)为T&。
4)否则,假设e的类型是T,则decltype(e) 为T。

int arr[5] = {0};
int *ptr = arr ;
struct S { double d; } s;
void Overloaded (int} ;
void Overloaded (char) ;  //重载的函数
int && RvalRef {}; 
const bool Func (int) ;
// 规则l:单个标记符表达式以及 访问类成员,推导为本类型
decltype (arr) varl ;                        // int[5], 标记符表达式
decltype (ptr) var2;                         // int*, 标记符表达式
decltype(s.d} var4 ;                         // double, 成员访问表达式
decltype (Overloaded) var5 ;                 //无法通过编译,是个重载的函数

//规则2:将亡值,推导为类型的右值引用
decltype (RvalRef ()) var6 = 1;              // int&&     

//规则3:左值,推导为类型的引用
decltype (true ? i : i) var7 = i;            // int&, 三元运算符,这里返回一个i的左值
decltype{(i}) var8 = i;                      // int&, 带圆括号的左值
decltype(++i) var9 = i;                      // int&, ++i返回i的左值
decltype(arr [3] ) var10 = i;                // int& [] 操作返回左值
decltype(*ptr) var11 = i;                    // int& *操作返回左值
decltype ("lval") var12 = "lval";            // const char(&) [9],字符串字面常量为左值

//规则4:以上都不是,推导为本类型
decltype(1) var13;                           // int, 除字符串外字面常量为右值
decltype(i++} var14;                         // int, i++返回右值
decltype ( (Func(1})) var15;                 // const bool, 圆括号可以忽略

is_rvalue_reference<decltype(RvalRef())>::value

is_lvalue_reference<decltype((i))>::value

is_const<decltype(ic)>::value

is_volatitle<decltype(b)>::value

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值