一、macro __cplusplus:检测编译器支持的版本
#include <iostream>
using namespace std;
int main() {
cout << "支持C++"<< __cplusplus << "版本" << endl;
return 0;
}
/*#define __cplusplus 201103L (支持C++11)
199711L (C++98)
*/
二、Variadic Templates(数量不定的模板参数)
注:可以传入任意数量的参数,而且不区分类型
原来的模板参数可以使类和函数的参数类型“任意化”,如果再加上“参数个数的任意化”,那么在参数方面的设计手段就基本上齐备了,有了variadic template 显然可以让设计出来的函数或是类有更大的复用性
。关键词 …
void printX() {} //当最后没有参数时结束打印(什么也不做),要写在前面以便递归最后一次调用(处理最后一次操作)
//参数个数逐一分开的特性
template <typename T, typename… Types> //Type表示任意多个不同的类型名
void printX(const T &firstArg, const Types&… args) //args表示任意多个Types类型的参数,因为Types表示任意多个不同的类型,所以args就是任意多个不同类型的参数
{
cout<<firstArg<<endl; //将传入printX的参数分为第一个参数和剩余参数,将第一个参数输出
printX(args…); //将剩余参数递归的传递给printX
}
须知道:
...就是一个所谓的pack(包)
用于template parameters,就是template parameters pack(模板参数包)
用于function parameter types,就是function parameter types pack(函数参数类型包)
用于function parameters,就是function parameters pack(函数参数包)
三、Space in Template Expressions(The requirement to put a space between two closing template expressions has gone)
即:在两个结束模板表达式之间放置空格的要求有所改变,在11版本之前需要加空格,之后不需要
vector<list<int> >; //OK in each C++ version
vector<list<int>>; //OK since C++11
四、nullptr and std::nullptr_t
C++11使用nullptr目的是为了代替NULL或者0 。
在某种意义上来说,传统 C++ 会把 NULL、0 视为同一种东西,这取决于编译器如何定义 NULL,有些编译器会将 NULL 定义为 ((void*)0),有些则会直接将其定义为 0。
nullptr 的类型为 nullptr_t,此类型定义在<csddef>中,能够隐式的转换为任何指针或成员指针的类型,也能和他们进行相等或者不等的比较。
//0 NULL nullptr区别:
void fun(int);
void fun(void*);
fun(0); //call fun(int)
fun(NULL); //call fun(int),会报警告(歧义)
fun(nullptr); //call fun(void*)
五、Automatic Type Deduction with auto(自动类型推导)
With C++11,you can declare a variable or an object without specifying its specific type by using auto.
使用 auto 进行类型推导的一个最为常见而且显著的例子就是迭代器。在以前我们需要这样来书写一个迭代器:
//C++11以前
vector<int>::iterator it = v.begin();
//C++11之后
auto it = v.begin();
C++11 Lambda表达式返回值自动推导:
auto arg = [](int x)->bool{
...,
};
//The latter is an object, representing a lambda!
六、Uniform Initialization(一致初始化)
C++11之前初始化格式不一致,11之后做了规范化(变量后面直接放{}做初始化)
int a[]{1,2,3};
vector<int> v{1,2,3,4};
注:编译器看到一个{t1, t2, t3}便做出一个initializer_list<T>,它关系到一个array<T, n>
七、Initializer Lists
C++11引入了初始化列表来初始化变量和对象。自定义类型,如果想用初始化列表就要包含initializer_list头文件。
initializer_list<>
To support the concept of initializer lists for user-defined types, C++11provides the class template std::initializer_list<>.It can be used to support initializations by a list of values or in any other place where you want to process where you want to process just a list of values.
//把std::initializer_list<int>当作容器使用
void print(std::initializer_list<int> vals){
for(auto p = vals.begin(); p != vals.end(); ++p){
std::cout << *p << "\n";
}
}
print({1,2,3,4,5});
class P{
public:
P(int a, int b){
cout << "P(int a, int b)" << endl;
}
P(initializer_list<int> initList){
cout << "P(initializer_list<int> initList)" << endl;
cout << "values= " << endl;
for(auto i : initList){
cout << i << " ";
}
cout << endl;
}
};
//call
P p(77, 5); //P(int, int)
P q{77, 5}; //P(initializer_list<int>);
P r = {77, 5}; //P(initializer_list<int>);
八、explicit for ctors taking more than one argument(在超过一个参数的构造函数前加入explicit)
首先要了解隐式转换:可以用单个实参来调用的构造函数定义了从形参类型到该类类型的一个隐式转换。
在C++中,explicit关键字用来修饰类的构造函数,被修饰的构造函数的类,不能发生相应的隐式类型转换。
在C++中,如果一个类有只有一个参数的构造函数,C++允许一种特殊的声明类变量的方式。在这种情况下,可以直接将一个对应于构造函数参数类型的数据直接赋值给类变量,编译器在编译时会自动进行类型转换,将对应于构造函数参数类型的数据转换为类的对象。如果在构造函数前加上explicit修饰词,则会禁止这种自动转换,在这种情况下,即使将对应于构造函数参数类型的数据直接赋值给类变量,编译器也会报错。
九、range-based for statement(for循环的特殊写法)
//写法:
for(decl : coll){
statement
}
//简单使用1
for(int i : {1, 2, 3, 4, 5, 6, 7}){
cout << i << endl;
}
//简单使用2
vector<double> vec;
for(auto elem : vec){
cout << elem << endl;
}
note:no explicit type conversions are possible when elements are initialized as decl inside the for loop. Thus, the following does not compile:
//避免隐式转换,在构造函数前加explicit
class C{
public:
explicit C(const string& s); //explicit(!) type conversion from strings
...
};
vector<string> vs;
for(const C& elem : vs){
//error: no conversion from string to C defined
cout << elem << endl;
}
十、=default、=delete
如果自行定义了一个ctor,那么编译器就不会再给一个default ctor。如果你强制加上=delete,就可以重新获得并使用default ctor
#include <iostream>
using namespace std;
class DataOnly {
public:
DataOnly () {}
~DataOnly() {}
DataOnly(const DataOnly & rhs) = delete; //禁止使用该函数
DataOnly& operator=(const DataOnly & rhs) = delete; //禁止使用该函数
DataOnly(const DataOnly && rhs) = default; //报错,因为默认移动构造不能有多个
DataOnly& operator=(DataOnly&& rhs) {}
};
int main(int argc, char *argv[]) {
DataOnly data1;
DataOnly data2(data1); // error: call to deleted constructor of 'DataOnly'
DataOnly data3 = data1; // error: call to deleted constructor of 'DataOnly'
return 0;
}
//=delete 关键字可用于任何函数,不仅仅局限于类的成员函数
void func1() = default; // 报错,default不能用在普通函数上
void func2() = delete; // 可以,但此时无意义
Big-Three 到 Big-five以及他们的delete和default
三大“巨头”函数分别为拷贝构造函数、拷贝赋值函数和析构函数,然后11-14增加到5个“巨头”函数,增加的另外2个是:构造函数、普通函数。
十一、Alias Template(template typedef)
用using声明别名化,且这个别名化是可以带参数的,同样具有别名化意思的还有define(也可以带参数),typedef(不能带参数),但它们在特殊时候都无法代替using,但是不能对别名化的名称做特化或偏特化,只能对原名做(化名不能代替本尊)。
Type Alias(similar to typedef)
/*case1
typedef void(*func)(int, int);
*/
using func = void(*)(int, int);
/*case2
type alias can introduce a member typedef name
*/
template<typename T>
struct Container{
using value_type = T; //typedef T value_type;
};
//which cab be used in generic programming
template<typename Cntr>
void fun2(const Cntr& c){
typename Cntr::value_type n;
}
/*alias template
type alias used to hide a template parameter
*/
template<class CharT>
using myString = std::basic_string<CharT, std::char_traits<CharT>>;
myString<char> str;
注:<string>和<string_fwd.h>都有以下typedef
typedef basic_string<char> string;
There is no difference between a type alias declaration and typedef declaration. This declaration may appear in block scope, class scope, or namespace scope.