1. auto类型推导
auto variable = value; //根据value自动推导变量类型
使用auto关键字以后,编译器会在编译期间自动推导出变量的类型,这样我们就不用手动指明变量的数据类型了。
- auto仅是一个占位符,在编译期间或被真正的类型替代
- 使用 auto 类型推导的变量必须马上初始化
- auto不能再函数参数中使用
- auto不能作用与类的非静态成员变量(也就是说只能作用于static修饰的成员变量中)
- auto关键字不能定义数组
- auto不能作用于模板参数
int x = 0;
auto p1 = &x; //auto推导为int*
auto &p2 = x; //auto推导为int
auto p3 = p2; //auto推导为int
const auto p4 = x; //auto推导为int
auto p5 = p4; //auto推导为int,无引用,const属性被抛弃
const auto &p6 = x; //auto推导为int
auto &p7 = p6; //auto推导为cons int,因为有引用
eg1:
//auto定义迭代器
#include<iostream>
#include<vector>
using namespace std;
int main(){
vector<vector<int>> v;
vector<vector<int>>::iterator i = v.begin();//定义迭代器
auto j = v.begin();//使用auto简化定义迭代器
return 0;
}
eg2:
//auto泛型编程
#include<iostream>
using namespace std;
class A{
public:
static int get(void){
return 100;
}
};
class B{
public:
static const char* get(void){
return "hello world";
}
};
//函数模板
template <typename T>
void func(void){
auto val = T::get(); //auto根据T类型进行推导
cout<<val<<endl;
}
int main(){
func<A>(); //函数模板中的类型T替换为A
func<B>(); //函数模板中的类型T替换为B
return 0;
}
//如果不使用auto
//template<typename T1, typename T2>
//void func(void){
// T2 val = T1::get();
// cout<<val<<endl;
//}
//func<A, int>();
//func<B, const char*>();
2. decltype类型推导
decltype(exp) variable = value; //decltype(exp) variable;
variable表示变量名,value表示赋给变量的值,exp表示一个表达式(exp的结构是有类型的,不能是void)。也是在编译时期进行类型推导,但decltype根据exp表达式推导出变量的类型,跟value没有关系,且不要求变量必须初始化。
- 如果 exp 是一个不被括号
( )
包围的表达式,或者是一个类成员访问表达式,或者是一个单独的变量,那么 decltype(exp) 的类型就和 exp 一致 - 如果 exp 是函数调用,那么 decltype(exp) 的类型就和函数返回值的类型一致
- 如果 exp 是一个左值,或者被括号
( )
包围,那么 decltype(exp) 的类型就是 exp 的引用;假设 exp 的类型为 T,那么 decltype((exp) 的类型就是 T&
3. 返回值类型后置
考虑到泛型编程中,可能需要通过参数的运算来得到返回值的类型,因此,在 C++11中增加了返回类型后置(trailing-return-type,又称跟踪返回类型)语法,将 decltype 和 auto 结合起来完成返回值类型的推导。返回值类型后置语法,是为了解决函数返回值类型依赖于参数而导致难以确定返回值类型的问题。有了这种语法以后,对返回值类型的推导就可以用清晰的方式(直接通过参数做运算)描述出来。
//template<typename R, typename T, typename U>
//R add(T t, U u){
// return t+u;
//}
//int a = 1; float b = 2.0;
//auto c = add<decltype(a+b)>(a, b); //这样使用就比较繁琐
//eg1:
template<typename T, typename U>
auto add(T t, U u) -> decltype(t+u){
return t+u;
}
//eg2:
int& foo(int& i);
float foo(float& f);
template<typename T>
auto func(T& val) -> decltype(foo(val)){
return foo(val);
}
4. 使用using定义别名
C++中可以使用typedef
重定义一个类型,但是无法重定义一个模板。
typedef unsigned int unit_t; //仅仅是原有的类型取了一个新名字
using unit_t = unsigned int; //using语法覆盖了typedef的全部功能
且using
可以重定义一个模板:
template<typename T>
using str_map_t = std::map<std::string, T>;
//...
str_map_t<int> map_str_int; //定义一个<str,int>的map映射
5. 支持函数模板的默认模板参数
template<typename T1 = int, typename T2> //c++98中出错,但是c++11中可以使用
T1 func(T2 val){
return val;
}
int main(){
func(1); // T1为int,T2根据1推导为int
func<char>(1); //T1为char,T2根据1推导为int
func<double, int>(97);
return 0;
}
当所有模板参数都有默认参数时,函数模板的调用如同一个普通函数。但对于类模板而言,哪怕所有参数都有默认参数,在使用时也必须在模板名后跟随<>
来实例化。可以指定函数中的一部分模板参数采用默认参数,而另一部分使用自动推导。如果模板参数即无法推导出来,又未设置其默认值,则编译器直接报错。
6. tuple元组详解
C++11 标准新引入了一种类模板,命名为 tuple,tuple 最大的特点是:实例化的对象可以存储任意数量、任意类型的数据。
1).使用构造函数构造tuple模板类对象
#include<iostream>
#include<tuple>
using std::tuple
int main(){
std::tuple<int, char> first; //默认构造
std::tuple<int, char> second(first);
std::tuple<int, char> third(std::make_tuple(20, 'b'));
std::tuple<long, char> fourth(third);
std::tuple<int, char> fifth(10, 'a');
std::tuple<int, char> sixth(std::make_pair(30, 'c'));
return 0;
}
2).make_tuple()函数
auto first = std::make_tuple(10, 'a'); //first:tuple<int,char>
const int a = 0; int b[3];
auto seconde = std::make_tuple(a, b); //second:tuple<int,int*>
3).tuple对象常用函数
函数或类模板 | 描 述 |
---|---|
tup1.swap(tup2) swap(tup1, tup2) | tup1 和 tup2 表示类型相同的两个 tuple 对象,tuple 模板类中定义有一个 swap() 成员函数, 头文件还提供了一个同名的 swap() 全局函数。 swap() 函数的功能是交换两个 tuple 对象存储的内容。 |
get(tup) | tup 表示某个 tuple 对象,num 是一个整数,get() 是 头文件提供的全局函数,功能是返回 tup 对象中第 num+1 个元素。 |
tuple_size::value | tuple_size 是定义在 头文件的类模板,它只有一个成员变量 value,功能是获取某个 tuple 对象中元素的个数,type 为该tuple 对象的类型。 |
tuple_element<I, type>::type | tuple_element 是定义在 头文件的类模板,它只有一个成员变量 type,功能是获取某个 tuple 对象第 I+1 个元素的类型。 |
forward_as_tuple<args…> | args… 表示 tuple 对象存储的多个元素,该函数的功能是创建一个 tuple 对象,内部存储的 args… 元素都是右值引用形式的。 |
tie(args…) = tup | tup 表示某个 tuple 对象,tie() 是 头文件提供的,功能是将 tup 内存储的元素逐一赋值给 args… 指定的左值变量。 |
tuple_cat(args…) | args… 表示多个 tuple 对象,该函数是 头文件提供的,功能是创建一个 tuple 对象,此对象包含 args… 指定的所有 tuple 对象内的元素。 |
tuple 模板类对赋值运算符 = 进行了重载,使得同类型的 tuple 对象可以直接赋值。此外,tuple 模板类还重载了 ===、!=、<、>、>=、<= 这几个比较运算符,同类型的 tuple 对象可以相互比较(逐个比较各个元素。
7. 列表初始化
在 C++11 中,初始化列表的适用性被大大增加了,它现在可以用于任何类型对象的初始化,不仅统一了各种对象的初始化方式,而且还使代码的书写更加简单清晰。
class Foo{
public:
Foo(int) {}
private:
Foo(const Foo &);
};
struct A
{
int x;
struct B
{
int i;
int j;
} b;
} a { 1, { 2, 3 } }; //POD类型
int main(void){
Foo a1(123);
//Foo a2 = 123; //error: 'Foo::Foo(const Foo &)' is private
//a3、a4 使用了新的初始化方式来初始化对象,效果如同 a1 的直接初始化
Foo a3 = { 123 }; //虽然使用了等于号,但它仍然是列表初始化
Foo a4 { 123 }; //{}前面的等于号是否书写对初始化行为没有影响
//a5、a6 则是基本数据类型的列表初始化方式
int a5 = { 3 };
int a6 { 3 };
int i_arr[3] { 1, 2, 3 };
int* a = new int { 123 };
double b = double { 12.12 };
int* arr = new int[3] { 1, 2, 3 };
return 0;
}
8. lambda匿名函数
[外部变量访问方式说明符] (参数) mutable noexcept/throw() -> 返回值类型
{
函数体;
};
1)[外部变量方位方式说明符]:向编译器表明当前是一个 lambda 表达式,其不能被省略。
2)(参数):可省略,和普通函数的定义一样,lambda 匿名函数也可以接收外部传递的多个参数。和普通函数不同的是,如果不需要传递参数,可以连同 () 小括号一起省略。
3)mutable:可省略,如果使用则之前的 () 小括号将不能省略。默认情况下,对于以值传递方式引入的外部变量,不允许在 lambda 表达式内部修改它们的值。如果想修改它们,就必须使用 mutable 关键字。
4)noexcept/throw():可省略,如果使用,在之前的 () 小括号将不能省略。默认情况下,lambda 函数的函数体中可以抛出任何类型的异常。而标注 noexcept 关键字,则表示函数体内不会抛出任何异常;使用 throw() 可以指定 lambda 函数内部可以抛出的异常类型。
5)-> 返回值类型:可省略,指明 lambda 匿名函数的返回值类型。值得一提的是,如果 lambda 函数体内只有一个 return 语句,或者该函数返回 void,则编译器可以自行推断出返回值类型,此情况下可以直接省略-> 返回值类型
。
6)函数体:可省略,和普通函数一样,lambda 匿名函数包含的内部代码都放置在函数体中。在 lambda 表达式内可以使用任意一个全局变量,必要时还可以直接修改它们的值。
[外部变量]的定义方式:
外部变量格式 | 功能 |
---|---|
[] | 空方括号表示当前 lambda 匿名函数中不导入任何外部变量。 |
[=] | 只有一个 = 等号,表示以值传递的方式导入所有外部变量; |
[&] | 只有一个 & 符号,表示以引用传递的方式导入所有外部变量; |
[val1,val2,…] | 表示以值传递的方式导入 val1、val2 等指定的外部变量,同时多个变量之间没有先后次序; |
[&val1,&val2,…] | 表示以引用传递的方式导入 val1、val2等指定的外部变量,多个变量之间没有前后次序; |
[val,&val2,…] | 以上 2 种方式还可以混合使用,变量之间没有前后次序。 |
[=,&val1,…] | 表示除 val1 以引用传递的方式导入外,其它外部变量都以值传递的方式导入。 |
[this] | 表示以值传递的方式导入当前的 this 指针。 |
#include <iostream>
#include <algorithm>
using namespace std;
int all_num = 0;//全局变量
int main(){
int num[4] = {4,1,3,2};
sort(num, num+4, [=](int x, int y) -> bool{return x < y;}); //从小到大排序,如果不使用lambda表达式比,则需要sort(num,n num+4,sort_up), sort_up() 函数的功能和上一个程序中的 lambda 匿名函数完全相同
//lambda匿名函数没有函数名称,可以手动设置一个
auto display = [](int a, int b) -> void{cout<<a<<" "<<b;};
display(10,20); //调用lambda函数
//值传递和引用传递
int num_1 = 1;
auto lambda_1 = [=]{
all_num = 10; //全局变量再lambda函数体中可以修改
cout<<num_1<<endl; //函数体内只能使用值传递的外部变量
};
lambda_1();
auto lambda_2 = [&]{
all_num = 100; //全局变量可以修改
num_1 = 10; //函数体内可以修改使用引用传递的外部变量
cout<<num_1<<endl;
};
lambda_2();
auto lambda_3 = [=] mutable{ //加上mutable关键字
num_1 = 20; //这里只是修改的num_1拷贝的那一份,外部的num_1还是没有被修改;
cout<<num_1<<endl; //输出20
};
lambda_3();
return 0;
}
9. 非受限联合体(union)
联合体(Union)是一种构造数据类型。在一个联合体内,我们可以定义多个不同类型的成员,这些成员将会共享同一块内存空间。C++11 标准规定,任何非引用类型都可以成为联合体的数据成员,这种联合体也被称为非受限联合体。
class Student{
public:
Student(bool g, int a): gender(g), age(a) {}
private:
bool gender;
int age;
};
union T{
Student s; // 含有非POD类型的成员,gcc-5.1.0 版本报错
char name[10];
};
int main(){
return 0;
}
10. for循环
for(表达式 1; 表达式 2; 表达式 3){
//循环体
}
C++ 11 标准中,除了可以沿用上面介绍的用法外,还为 for 循环添加了一种全新的语法格式:
for (declaration : expression){
//循环体
}
declaration表示此处要定义一个变量,该变量的类型为要遍历序列中存储元素的类型,变量类型可以用auto关键字表示来推导;expression表示要遍历的序列,常见的可以为事先定义好的普通数组或者容器,还可以是用 {} 大括号初始化的序列。旧格式的 for 循环可以指定循环的范围,而 C++11 标准增加的 for 循环,只会逐个遍历 expression 参数处指定序列中的每个元素。
在使用新语法格式的 for 循环遍历某个序列时,如果需要遍历的同时修改序列中元素的值,实现方案是在 declaration 参数处定义引用形式的变量,如果不需要修改值,建议定义const & 常引用形式,避免底层复制变量的过程,效率更高
:
#include<iostream>
#include<vector>
using namespace std;
int main(){
char arc[] = "abcde";
vector<char>myvector(arc, arc+5);
for(auto &ch : myvector){ //定义引用形式
ch++;
}
for(auto const &ch : myvector){
cout<<ch;
}//输出:bcdef
return 0;
}
参考
1.http://c.biancheng.net/cplus/11/