c++2.0新特性

此文档整理c++11中新特性.

语言:

  • Variadic Templates
  • move Semantics
  • auto
    • auto和decltype的区别与联系
  • range base for
  • Initializer list
  • Lambdas
  • type_traits
  • Unordered Containers
  • forward_list
  • array
  • explicit
  • tuple
  • Con-currency
  • RegEx

Initializer list

1
2
3
4
int values[]{1,2,3};
vector<int> v{2,3,4,5};
vector<string>cities{"hello","hi"};
complext<double> c{4.0, 3.0};

当编译器看到{t1,t2,…,tn}便做出一个initializer_list,它关联至一个array<T,n>.调用函数(如ctor)时该array内的元素可被编译器分解逐一传给函数.但是,若函数的参数是initializer_list<T>,调用者
却不能给予数个T参数然后以为它们会被自动转为一个initializer_list<T>传入.

对于vector<string>cities{“hello”,”hi”}; {“hello”,”hi”}形成了一个initializer_list<string>,背后有个array<string,2>,调用vector<string> ctor时编译器找到一个vector<string> ctor接受initializer_list<string>.所有容器都有这样的ctor.

complext<double> c{4.0, 3.0}; 这形成一个initializer_list<string>,背后有个array<double,2>.调用complext<double> ctor时该array内的2个元素被分解传给ctor.因为complex<double>并无任何ctor接受initializer_list.

initializer_list的使用

在函数中使用initializer_list传递不定个参数

1
2
3
4
5
6
7
8
9
10
void print(initializer_list<int> vals) {
for (auto p = vals.begin(); p != vals.end(); ++p) {
cout << *p << " ";
}
cout << endl;
}

void test() {
print({12, 3, 5, 7, 13, 17});
}

在类中使用initializer_list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class P {
public:
P(int a, int b) {
cout << "P(int, int): a=" << a << " b=" << b << endl;
}

P(initializer_list<int> initlist) {
cout << "P(initializer_list<int>), values=";
for (auto i:initlist) {
cout << i << " ";
}
cout << endl;
}
};

void test1() {
// 若同时存在两个构造函数,如上
P p(77, 5); // 调用构造函数 P(int, int): a=77 b=5
P q{77, 5}; // 调用构造函数 P(initializer_list<int>), values=77 5
P r{77, 5, 42}; // P(initializer_list<int>), values=77 5 42
P s = {77, 5}; // P(initializer_list<int>), values=77 5
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void test2() {
// 若仅存在构造函数P(int, int)
P p(77, 5); // 调用构造函数 P(int, int): a=77 b=5
P q{77, 5}; // 调用构造函数 P(initializer_list<int>), values=77 5
// P r{77, 5, 42}; // 无法使用这种方式创建对象
P s = {77, 5}; // P(initializer_list<int>), values=77 5
}

void test3() {
// 若仅存在构造函数P(initializer_list<int> initlist)
// P p(77, 5); // 无法使用这种方式创建对象
P q{77, 5}; // P(initializer_list<int>), values=77 5
P r{77, 5, 42}; // P(initializer_list<int>), values=77 5 42
P s = {77, 5}; // P(initializer_list<int>), values=77 5
}

在标准库容器和算法中使用initializer_list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
vector<int> v1{11, 22, 454, 33};
vector<int> v2({11, 22, 454, 33});
for (auto i:v2) {
cout << i << " ";
}
cout << endl;

vector<int> v3{1, 2, 3};
v3.insert(v3.begin() + 1, {11, 22, 33, 55});
for (auto i:v3) {
cout << i << " ";
}
cout << endl;

cout << max({string("hello"), string("good"), string("stacy"), string("nice")}) << endl;
cout << min({string("hello"), string("good"), string("stacy"), string("nice")}) << endl;
cout << max({11, 24, 25, 44, 10}) << endl;

initializer_list的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
template<class _E>
class _initializer_list {
public:
typedef _E value_type;
typedef const _E &reference;
typedef const _E &const_reference;
typedef size_t size_type;
typedef const _E *iterator;
typedef const _E *const_iterator;

private:
iterator _M_array; // array的头
size_type _M_len; // array的长度

// 编译器实现准备好一个array,并把array的首地址和长度记录下来
constexpr _initializer_list(const_iterator _a, size_type _l) :
_M_array(_a), _M_len(_l) {}

public:
constexpr _initializer_list() noexcept :
_M_array(0), _M_len(0) {}

constexpr size_type size() const noexcept { return _M_len; }

constexpr const_iterator begin() const noexcept {
return _M_array;
}

constexpr const_iterator end() const noexcept {
return begin() + _M_len;
}
};

explicit

对于仅有单一实参(也可能是有n(n>=2)个参数,但是n-1个参数都有默认实参)的构造函数,指定expliciit表示不允许隐式转换.

对于多个实参的构造函数,指定explicit表示不允许进行隐式转换.
如果没有指定explicit,那么对于多个实参的构造函数也可以进行隐式转换.

1
2
3
4
5
6
7
8
9
10
11
12
class P {
public:

P(int a, int b, int c) {
cout << "a=" << a << " b=" << b << " c=" << c << endl;
}
};

void test1() {
// 多实参的隐式转换
P p = {1, 2, 3}; //
}

运行结果:

1
a=1 b=2 c=3

Alias Template(template typedef)

与模板模板参数相关.

1
2
3
4
5
template<typename T>
using Vec = std::vector<T, MyAlloc<T>>; //  为vector指定别名

// 使用别名
Vec<int> coll;

type Alias

1
2
3
4
5
6
7
8
typedef void(*func)(int, int);		// func是一个返回值为void,两个int参数的函数指针类型

// 下面是等价写法
using func = void (*)(int,int);

// 给函数指针赋值
void example(int,int){}
func fn = example;

using

using的三种用法

using-directives

1
2
3
using namespace std;

using std::cout;

using-declarations

1
2
3
4
5
class Derived:public Base{
protected:
using Base::_M_alloc;
using Base::_M_deallocate;
}

type alias and alias template

1
2
3
4
template<typename>
struct Container{
using value_type = T;
}
1
2
3
4
5
// type alias used to hide a template parameter
template<class CharT> using mystring = std::basic_string<CharT,std::char_traits<CharT>>;

// mystring是模板,mystring<char>才是类型
mystring<char> str;

noexcept

final

final有两种用法:

  1. 修饰class,禁止类被继承
  2. 修饰virtual function,禁止virtual function被重写

decltype

decltype可以找出一个表达式的类型.
decltype有三种用法:

  1. 声明一个type作为返回类型

    1
    2
    3
    4
    template<typename T1, typename T2>
    auto add(T1 x, T2 y) -> decltype(x + y) {
    return x + y;
    }
  2. 在metaprogramming中使用

    取一个表达式的类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    template<typename T>
    void test_decltype(T obj) {
    typedef typename decltype(obj)::iterator iType; // 相当于typedef typename T::iterator iType
    iType it = obj.begin();
    if (it != obj.end()) {
    cout << "not empty container" << endl;
    } else {
    cout << "empty container" << endl;
    }

    decltype(obj) anoObj(obj);
    cout << "anoObj.size(): " << anoObj.size() << endl;
    cout << endl;
    }
  3. 面对lambda,往往只有object,没有type,要获得其类型就需要借助于decltype.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class Person {
    public:
    Person(string _firstName, string _lastName) : fname(_firstName), lname(_lastName){}

    string firstName() const { return fname; }

    string lastName() const { return lname; }

    private:

    string fname;
    string lname;
    };

    auto comp = [](const Person &p1, const Person &p2) {
    return p1.lastName() < p2.lastName()
    || (p1.lastName() == p2.lastName() && p1.firstName() < p2.firstName();
    };
    set<Person, decltype(comp)> coll(comp);

    lambda