变参模板

变参模板(Variadic Templates),能够接受任意参数的模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// example 1
void print() {}

template <typename T, typename... Types>
void print(const T& firstArg, const Types&... args)
{
cout << firstArg << " " << sizeof...(args) << endl; // 每次能处理的只有 firstArg
print(args...); // args...只能往下传递
}

// ----------------------------------------------------
// ...就是一个所谓的 pack(包)
// 用于 template parameters,就是 template parameters pack (模板参数包)
// 用于 function parameter types,就是 function parameter types pack (函数参数类型包)
// 用于 function parameters,就是 function parameters pack (函数参数包)

// 加上...表示可以接受任意个参数,要注意其位置;
// 一定要写一个处理最后情况的函数,这其实就像是递归,必须处理最简单的情况

实际使用1:

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
33
34
35
#include <functional>

class CustomerHash {
public:
std::size_t operator()(const Customer& c) const {
return hash_val(c.fname, c.lname, c.no);
}
};

// 1. 最泛化的,设置 seed 后开始递归,不设置 seed 根本进不去 2.
template <typename... Types>
inline size_t hash_val(const Types&... args) {
size_t seed = 0;
hash_val(seed, args...);
return seed;
}

// 2. 递归函数
template <typename T, typename... Types>
inline void hash_val(size_t& seed, const T& val, const Types&... args) {
hash_combine(seed, val);
hash_val(seed, args...);
}

// 3. 递归函数的结尾
template <typename T>
inline void hash_val(size_t& seed, const T& val) {
hash_combine(seed, val);
}

// 4. 实际计算 hash 的函数
template <typename T>
inline void hash_val(size_t& seed, const T& val) {
seed ^= std::hash<T>(val) + 0x9e3779b9 + (seed<<6) + (seed>>2);
}

实际使用2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template <typename... Values> class tuple;  // 泛化
template <> class tuple<> {}; // 特化

template <typename Head, typename... Tail>
class tuple<Head, Tail...> : private tuple<Tail...>
{
typedef tuple<Tail...> inherited;
public:
tuple() {};
tuple(Head v, Tail... vtail):m_head(v), inherited(vtail...) {};

typename Head::type head() { return m_head; }
inherited& tail() { return *this; }
protected:
Head m_head;
};

模板表达式里的空间

nullptr 和 std::nullptr_t

一个新的关键字,空指针

1
2
3
4
5
void f(int);
void f(void *);
f(0); // calls f(int)
f(NULL); // calls f(int)
f(nullptr); // calls f(void *)

自动类型推断 auto

尽量只在类型过于复杂、书写太长,或者类型难以推断的地方

1
2
3
4
5
6
7
8
9
auto i = 42; // i has type int
double f();
auto d = f(); // d has type double

vector<string> v;
auto pos = v.begin(); // pos has type vector<string>::iterator,类型太长,懒得写
auto l = [](int x)->bool{ // l has type of a lambda,类型过于复杂
...,
};

Uniform Initialization

初始化可能在小括号、大括号和赋值操作符之间发生

1
2
3
4
5
6
7
int value[] {1,2,3};	// initializer_list<T>
vector<int> v {2,3,5,7,11,13,17}; // 容器中有可以接受initializer_list<T>的ctor
complex<double> c{4.0, 3.0};

Rect r1 = { 3,7,20,25,&area, &print };
Rect r1(3,7,20,25);
int ia[6] = { 27,210,12,47,109,83 };

Initializer Lists

Initializer Lists的一些示例

1
2
3
4
5
6
7
8
9
10
11
12
13
int i;		// i 的值不确定
int j{}; // j 被初始化为 0
int* p; // p 的值不确定
int* q{}; // q 被初始化为 nullptr
//
int x1(5.3); // OK, x1==5
int x2 = 5.3; // OK, x2==5
int x3{5.3}; // ERROR: narrowing
int x4 = {5.3}; // ERROR: narrowing
char c1{7}; // OK: 这里没有narrowing
char c2{99999}; // ERROR“ narrowing(99999不符合char的范围)
std::vector<int> v1{1,2,4,5}; // OK
std::vector<int> v2{1, 2.3, 4, 5.6}; // ERROR: narrowing

模板类std::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
// 示例1
void print(std::initializer_list<int> vals)
{
for (auto p=vals.begin(); p!=vals.end(); p++) {
std::cout << *p << "\n";
}
}
print({12,3,5,7,11,13,17}); // 传递a list of value给print()

// 示例2
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;
}
};

P p(77,5); // P(int, int), a=77, b=5
P q{77,5}; // P(initializer_list<int>), values= 77 5 42
P r{77,5,42}; // P(initializer_list<int>), values= 77 5
P s={77,5}; // P(initializer_list<int>), values= 77 5
// 如果没有initializer_list的ctor,q和s可以运行,但是r不行

explicit

explicit for ctors,表示这个ctor必须被明确调用才行,不允许编译器做隐式转换。

示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct Complex
{
int real, image;

explicit
Complex(int re, int im=0): real(re), image(im){}

Complex operator+(const Complex& x){
return Complex((real + x.real), (image + x.image));
}
}

Complex c1(12, 5);
Complex c2 = c1 + 5; // [Error] no match for 'operator+' (operand types are 'Complex' and 'int')
// 如果ctor没有explicit关键字,那么编译器就会把5隐式转换为5+0i,然后进行运算;

range-based for statement

1
2
3
for ( decl : coll ) {
statement
}
1
2
3
for (int i : { 2, 3, 5, 7, 9, 13, 17, 19}) {
cout << i << endl;
}
1
2
3
4
5
6
7
8
9
vector<double> vec;
...
for ( auto elem : vec ) {
cout << elem << endl;
}
// 取引用,可以直接修改值
for (auto& elem : vec) {
elem *= 3;
}

=default, =delete

如果自行定义了一个ctor,那么编译器就不会再给你一个default ctor;

如果你强制加上 =default,就可以重新获得并使用 default ctor;

1
2
3
4
5
6
7
8
9
10
11
12
class Zoo
{
public:
Zoo(int i1, int i2): d1(i1), d2(i2) {}
Zoo(const Zoo&) =delete; // 拷贝构造
Zoo(Zoo&&) =default;
Zoo& operator=(const Zoo&) =default; // 拷贝赋值
Zoo& operator=(const Zoo&&) =delete;
virtual ~Zoo() {}
private:
int d1, d2;
}

Alias Template (template typedef)

Alias:化名,别名

1
2
3
template <typename T>
using Vec = std::vector<T, MyAlloc<T>>;
Vec<int> coll;

等价于:

1
std::vector<int, MyAlloc<int>> coll;

typedef 没法指定类型参数

1
typedef std::vector<int, MyAlloc<int>> Vec;

Type Alias

和 typedef 相似

1
2
3
4
5
// typedef void (*func)(int, int); 等价于下式
using func = void(*)(int, int);
// func 表示函数指针
void example(int, int) {}
func fn = example;

using关键字

1
2
3
4
5
6
7
8
9
using namespace std;	// 命名空间
using std::cout; // 声明 namespace members
// type alias和alias template声明
using func = void(*)(int, int);
template<typename T>
struct Container {
using value_type = T;
};
template <class CharT> using mystring=std::basic_string<CharT, std::char_traits<CharT>>;

noexcept关键字

1
2
3
4
5
6
void foo() noexcept; <=> void foo() noexcept(true);

void swap(Type& x, Type& y) npexcept(noexcept(x.swap(y)))
{
x.swap(y);
}

保证函数不会丢出异常;noexcept(...)中可以指定boolean表达式,表面在某种情况下不丢出异常;

override关键字

作用于虚函数

1
2
3
4
5
6
struct Base{
virtual void vfunc(float) {}
};
struct Derived1: Base {
virtual void vfunc(int) {} // 本意是重写父类函数的,结果参数不对,编译器会认为是新的虚函数
}
1
2
3
4
struct Derived2: Base {
virtual void vfunc(int) override {} // 报错,想重写还传这个参数...
virtual void vfunc(float) override {} // 重写成功,参数一致
}

final关键字

用于类,则该类不能被继承;用于函数,则该函数不能被重写;

1
2
struct Base1 final {};
struct Derived1 : Base1 {}; // ERROR
1
2
3
4
5
6
7
struct Base2 {
virtual void f() final;
}

struct Derived2 : Base2 {
void f(); // ERROR
}

decltype

使用decltype,可以让编译器找到一个expression的类型

1
2
3
4
map<string, float> coll;
decltype(coll)::value_type elem;
// 等价于c++ 11之前的写法
map<string, float>::value_type elem;

用法:

  1. 声明返回类型

    1
    2
    3
    4
    5
    template <typename T1, typename T2>
    decltype(x+y) add(T1 x, T2 y); // 编译出错:编译器先看到x,y,但是前面没出现过
    =>
    template <typename T1, typename T2>
    auto add(T1 x, T2 y) -> decltype(x+y);
  1. 适用于metaprogramming

    1
    2
    3
    4
    5
    template <typename T>
    void test_decltype(T obj){} // T必须是容器

    typedef typename T::iterator iType;
    => typedef typename decltype(obj)::iterator iType;
  2. 传递 lambda 的类型

    1
    2
    3
    auto cmp = [](const Person& p1, const Person& p2) { ... };
    // 必须传递cmp给coll;否则coll会调用cmp的默认ctor,但cmp是lambda,没有默认构造函数,就会报错。所以必须这么传递
    std::set<Person, decltype(cmp)> coll(cmp);

Lambda

lambda就是可以在语句或表达式中定义的函数,这个函数可以被作为参数或者局部变量;

lambda是一个对象,功能上是一个函数;

1
2
3
4
5
6
7
8
[...](...) mutable throwSpec -> retType { ... }

[]:里面放取用的外部变量,值or引用都可
():标识重载的()操作符的参数,mutable,throwSpec,retType都省略的话()也可以省略
mutable:表示变量以value形式传入,且值可以修改,不加则传入的值只可读
throwSpec:指定函数抛出的异常,如 throw(int)
retType:返回类型,没有则编译器自己推断
{}:函数体
1
2
3
4
5
// 示例
auto I = []{
std::cout << "hello lambda" << std::endl;
};
I(); // prints "hello lambda"