void printX()
{}
template <typename T, typename... Types>
void printX(const T& firstArg, const Types&... args)
{
cout << firstArg << endl;
printX(args...);
}
通过
sizeof...(args)可以知道args中包括多少个参数
通过可变参数模板,可以帮助我们使用递归做一些操作
// 可变参数模板的用例
#include <functional>
template<typename T>
inline void hash_combine(size_t& seed. const T& val)// 函数_4
{
seed ^= std::hash<T>()(val) + 0x9e3779b9 + (seed<<6) + (seed >> 2);
}
template<typename T>
inline void hash_val(size_t& seed, const T& val){// 函数_3
hash_combine(seed, val);
}
template<typename T, typename... Types>
inline void hash_val(size_t& seed, const T& val, const Types&... args)// 函数_2
{
hash_combine(seed, val);
// 根据args...的参数个数来执行 函数_2 或者 函数_3
hash_val(seed, args... );
}
template<typename... Types>
inline size_t hash_val(const Types&... args)// 函数_1
{
size_t seed = 0;
// 虽然 函数_1 和 函数_2 都符合执行条件,但是 函数_2 相比而言更特化,所以执行 函数_2
hash_val(seed, args);
return seed;
}
class CustomerHash{
public:
std::size_t operator()(const Customer& c) const{
// 因为第一个fname不是size_t类型,所以执行的是 函数_1 hash_val
return hash_val(c.fname, c.lname, c.no);
}
}
这里用args配合递归不停的切分参数
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;
}
// tuple<int, float, string> t(41, 6.3, "Niko");
// t.head() -> 41
// t.tail() -> tuple<float, string>(6.3, "Niko")
// t.tail()->head() -> 6.3
这里最上面有个空的 tuple<>,是作为递归继承的中止点
这里用递归继承的方式做出了tuple
vector<list<int> > // 全版本支持
vector<list<int>> // C++11之后没有空格也行
因为老版本一些编译器会将
>>识别为操作符,所以需要加空格
C++11之后不加空格也行
nullptr 指的是 空指针,C++11允许用nullptr代替0或者NULL
void f(int);
void f(void*);
f(0); // 运行 f(int)
f(NULL); // 运行 f(int),如果是其他数,就不清楚具体调用哪个了
f(nullptr); // 云心 f(void*)
#if defined(__cplusplus) && __cplusplus >= 201103L
#ifndef __GXX_NULLPTR_T
#define __GXX_NULLPTR_T
typedef decltype(nullptr) nullptr_t;
auto i = 20; // i 的 类型是 int
double f();
auto d = f(); // d 的 类型是 double
vector<string> v;
auto pos = v.begin(); // pos 的 类型是 vector<string>::iterator
auto l = [](int x) -> bool {
// ...
}; // l 的类型是 lambda表达式
使用
auto主要是用在数据类型 太长 或者 太复杂 的情况 (比如迭代器、lambda的类型)
倒也不是什么情况都用auto
// 标准库中对auto的使用
#if __cplusplus >= 201103L
inline auto operator- (const reverse_iterator<_IteratorL> &__x,
const reverse_iterator<_IteratorR> &__y)
-> decltype(__y.base() - __x.base())
#else
inline typename reverse_iterator<_IteratorL>::difference_type
operator- (const reverse_iterator<_IteratorL>& __x,
const reverse_iteratror<_IteratorR>& __y)
#endif
初始化可能发生在小括号、中括号、赋值中
Rect r1 = {3, 4, 5}; // 大括号
Rect r1(3, 4, 5); // 小括号
int values[5] = {1, 3, 4, 5}; // 赋值
C++11引入一致性初始化——大括号(虽然用以前的也没错)
int values[]{1, 3, 4, 5};
vector<int> v{1, 3 ,4 ,5};
complex<duoble> c{3.0, 4.0};
变量之后直接加上大括号即表示初始化
当编译器看到{t1, t2, t3...}便做出一个initializer_list<T>,他关联至一个array<T, n>,调用函数(比如构造函数)时arry中的元素被逐个分解传给函数;如果调用函数的参数就是initializer_list<T>,就不会注意分解而是直接整个传给函数
比如:vector<int> v{1, 3 ,4 ,5}; 中的初始化参数 {1, 3 ,4 ,5}转换成 initlializer_list<int>,而这个关联array<int, 4>容器
int i; // 没有被初始化
int j{}; // j被初始化为0
int *p; // p没有被初始化
int *q{}; // q被初始化为nullptr
int x1(4.3); // ok
int x2 = 4.3; // ok
int x3 {4.3}; // 部分平台Error,部分平台warning
int x4 = {5.3}; // 部分平台Error,部分平台warning
char c1{7}; // ok
char c2{9999}; // 部分平台Error,部分平台warning
std::vector<int> v1{1, 3, 4, 5}; // ok
std::vector<int> v2{1.0, 3.0, 4.0, 5.0}; // 部分平台Error,部分平台warning
初始化列表的作用,还可以帮助检查一些错误
void print(std::initlializer_list<int> vals)
{
for(auto p = vals.begin(), p != vals.end(); ++p){
std::cout << *p << std::endl;
}
}
print({1, 3, 4, 5});
证明
{t1, t2, t3...}被转换成initializer_list<T>
class P{
public:
P(int a, int b){
std::cout << "(int ,int)" << std::endl;
}
P(initializer_list<int> initlist){
std::cout << "initializer_list" << std::endl;
}
};
P p(7, 7); // 输出 (int ,int)
P p{7, 7}; // 输出 initializer_list
P r{7, 5, 4}; // 输出 initializer_list
P p = {7, 7}; // 输出 initializer_list
如果不存在第二个
initializer_list的构造函数,则第三个对象 r 将创建失败
/**
* non-explicit-one-argument 只要一个实参就够了的构造函数
* 下面例子中:没有写oprator * (Fraction, int)的操作符重载也不会报错,因为编译器将4通过一个参数的构造变成Fraction(4, 1)类型
* 当构造函数前加上explicit关键字时,编译器不会进行隐式构造函数将4变成Fraction(4, 1)
*
* ——————————————————————————————————————————————————————————————————————————————
* 如果构造函数不存在explicit,且有operator double()转换函数,编译器报错,因为存在二义性即两种方式都可以行得通且没有优先级这么一说
* 如果构造函数存在explicit,且有operator double()转换函数,编译器不报错,因为只有一条路可走,将Fraction转换成double计算
* 如果构造函数不存在explicit,且没有operator double()转换函数,编译器不报错,因为只有一条路可走,将4转换成Fraction计算
**/
class Fraction
{
public:
// 这里构造函数只需要一个参数其实也够
Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den) {}
//explicit Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den) {}
Fraction operator*(const Fraction &f)
{
return Fraction(f.m_numerator * this->m_numerator, f.m_denominator * this->m_denominator);
}
std::string getString()
{
char numerator[10];
char denominator[10];
memset(numerator, 0, sizeof(numerator));
memset(denominator, 0, sizeof(denominator));
itoa(m_numerator, numerator, 10);
itoa(m_denominator, denominator, 10);
return std::string(std::string(numerator) + std::string("/") + std::string(denominator));
}
private:
int m_denominator; // 分母
int m_numerator; // 分子
};
int main()
{
Fraction f(3, 5);
Fraction f2 = f * 4;
std::cout << f2.getString() << std::endl;
system("pause");
return 0;
}
explicit告诉编译器,不能发生相应的隐式类型转换,只能以显示的方式进行类型转换
// for(decl : coll){
// statement
// }
for(int i : {2, 3, 4, 5, 6}){
cout << i << endl;
}
vector<double> vec;
//...
for(auto elem : vec){
cout << elem << endl;
}
for(auto &elem : vec){
elem *= 3;
}
// range-based for 的底层实现类似
for(auto _pos = coll.begin(), _end = coll.end(); _pos != _end; ++pos){
decl = *_pos;
// 对值的操作
}
// 或者
for(auto _pos = begin(coll), _end = end(coll); _pos != _end; ++pos){
decl = *_pos;
// 对值的操作
}
下面的写法会报错
class C{
public:
explicit C(const string& s); // 禁止string隐式转换成C
// ...
}
vector<string> vs;
for(const C& elem : vs){
cout << elem << endl; // 报错,因为字符串不能隐式的转换成C
}
如果自行定义一个构造函数,那么编译器不会再给一个默认构造函数
如果强制加上 =default 就可以重新获得并使用默认构造函数
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;
};
class Foo{
public:
Foo(int i) : _i(i) {}
Foo() = default; // 构造函数可以有多个,这里希望编译器提供默认构造
Foo(const Foo& x) : _i(x._i) {}
// Foo(const Foo&) = default; // Error 拷贝构造函数不可被重载
// Foo(const Foo&) = delete; // Error 拷贝构造函数不可被重载
Foo& operator=(const Foo& x) {_i = x._i; return *this;}
// Foo& operator=(const Foo& x) = default; // Error 赋值不可被重载
// Foo& operator=(const Foo& x) = delete; // Error 赋值不可被重载
void func1() = default; // Error 不可作为default
void func2() = delete; // ok 不过如果不需要可以不写这个函数…
~Foo() = default;
// ~Foo() = delete // 可以new不可delete
};
=default 除了默认成员函数(构造、析构、拷贝、赋值等)之外,其他函数使用编译报错(很明显,其他函数也没有所谓的默认)=delete可以用在任何函数上(=0只能用于virtual函数)一般而言,如果成员对象中存在指针,建议考虑深拷贝、浅拷贝的问题,需要自定义拷贝、赋值函数
template<typename T>
using Vec = std::vector<T, MyAlloc<T>>;
Vec<int> coll; // = std::vector<int, MyAlloc<int> coll;
使用该关键字可以找到表达式的参数类型
map<string, float> coll;
decltype(coll)::value_type elem; // C++11可以通过对象使用
map<string,float>::value_type elem; // C++11之前必须指明类型使用
template<typename T1, typename T2>
auto add(T1 x, T2 y) -> decltype(x+y); // 通过decltype动态设置返回参数类型
decltype(x+y) add(T1 x, T2 y); // 这样写报错,因为decltype先不知道x、y是什么,在后面的函数参数中才知道
auto cmp = [](const Person& p1, const Person& p2){
// 比较大小
};
std::set<Person, decltype(cmp)> coll(cmp);
[]{
std::cout << "hello world" << std::endl;
}(); // 输出 hello world
auto l = []{
std::cout << "hello world" << std::endl;
};
l();// 输出 hello world
[导入器](参数) mutable 异常 -> 返回类型 {
函数体
}
导入器,取用的外部变量(传值、传引用)
参数,正常函数参数
mutable 是否可以修改值
异常 一些特殊操作抛出异常
{
int id = 0;
// 值传递
auto f = [id]() mutable {
std::cout << "id : " << id << std::endl;
++id;// 如果没有mutable,则id不可执行++操作
};
id = 42;
f(); // 输出 id : 0
f(); // 输出 id : 1
f(); // 输出 id : 2
std::cout << id << std::endl; // 输出 id : 42
}
{
int id = 0;
// 引用传递
auto f = [&id](int param) {
std::cout << "id : " << id << std::endl;
++id; ++param;
};
id = 42;
f(7); // 输出 id : 42
f(7); // 输出 id : 43
f(7); // 输出 id : 44
std::cout << id << std::endl; // 输出 id : 45
}
{
int id = 0;
auto f = [id]() {
std::cout << "id : " << id << std::endl;
++id;
};
// 报错 没有mutable 不可更改
id = 42;
f();
f();
f();
std::cout << id << std::endl;
}
// 一个 = 表示 其他所有对象都是值传递, id为引用传递
auto f = [=, &id]{
// ...
};