C++11: 新手易学,老兵易用
2013-12-21 23:13
281 查看
1 C++11中针对vector<pair<int,string> >两个尖括号间的空格去除,也就是说C++11中可以写成:vector<pair<int,string>>,编译器这里不会将>>认为是右移符号。
2 auto: C++中每个变量使用前必须定义从而被视为静态语言,而不像一些脚本语言变量可以这样用x=1称为动态语言。 静态语言和动态语言主要区别:静态语言类型检查发生在编译阶段,动态语言类型检查发生在运行阶段。C++11中实现了auto和decltype从静态语言向动态语言拉近了一点。注意的是auto不再是存储类型的标识符,还记得曾几何C语言中大讲atuo、static等等变量的区别,现在auto在C++11中是个类型指示符,原来C++98的存储类型标识符语义被遗弃。auto告诉编译器所声明的变量类型在编译时期推导而得,相当于atuo是个占位符,等编译时确定变量的类型。如:atuo
x=1; 编译时x的类型自动推断为int,其等价于int x=1;
一个实用之处就是std::vector<int>::iterator it=vec.begin();现在可以写为: auto it=vec.begin()
atuo增加了类型的灵活性,比如:
auto在同一个语句中推断多个变量,第一个atuo的类型就是后面变量的了性,从左至右推导,如:
3 decltype:类型推导
typeid(类名).name()可以返回类型的名字,typeid(类名).hash_code()返回类型对应的唯一hash值,该hash值可以比较,如:typeid(类1).hash_code()==typeid(类名2).hash_code()。is_same<类名1,类名2>::value也可以判断两个类是否相同。
decltype和using结合的例子:
1) 当e是一个没有带括号的标记符表达式(除去关键字、字面量等编译器需要使用的标记之外的程序员自定义的标记)或者类成员访问表达式,那么decltype(e)就是e所命名的实体的类型。如果e是一个被重载的函数名,则会编译错误。
2) 否则,假设e类型是T,若e是一个将亡值(如函数非引用返回值),那么decltype(e)为T&&
3) 否则,若e是一个左值,decltype(e)为T&,这是一个左值规则
4) 否则,decltype(e)为T
例子如下:
4 追踪返回类型
先看一个实例:
#include <iostream>
using namespace std;
template<typename T1, typename T2>
auto Sum(const T1 & t1, const T2 & t2) -> decltype(t1 + t2)
{//当模板的返回值类型是随着模板实例化才能确定时,通过auto和decltype追踪返回值类型,auto相当于占位符,最后模板实例化从左至右,最后decltype推导出返回值类型填写回auto处即可
return t1 + t2;
}
template <typename T1, typename T2>
auto Mul(const T1 & t1, const T2 & t2) -> decltype(t1 * t2)
{
return t1 * t2;
}
int main()
{
auto a = 3;
auto b = 4L;
auto pi = 3.14;
auto c = Mul(Sum(a, b), pi);//完全根据模板实例化自动推导参数
cout << c << endl; // 21.98
}
追踪返回类型用于简化函数定义:
#include <type_traits>
#include <iostream>
using namespace std;
// 有的时候,你会发现这是面试题
int (*(*pf())())() {//这个看起来真的比较蛋疼
return nullptr;
}
// auto (*)() -> int(*) () 一个返回函数指针的函数(假设为a函数)
// auto pf1() -> auto (*)() -> int (*)() 一个返回a函数的指针的函数
auto pf1() -> auto (*)() -> int (*)() {//从右向左解析
return nullptr;
}
int main() {
cout << is_same<decltype(pf), decltype(pf1)>::value << endl; // 1
}
追踪返回值类型用于转发函数中:
5 for循环
标准库已经有了for_each
#include <vector>
#include <iostream>
using namespace std;
int main() {
vector<int> v = {1, 2, 3, 4, 5};
for (auto i = v.begin(); i != v.end(); ++i)
cout << *i << endl; // i是迭代器对象
for (auto e: v)//冒号分开,前面部分是范围内用于迭代的变量,后半部分是迭代的范围,只能用于明确已知迭代范围
cout << e << endl; // e是解引用后的对象
}
对于迭代范围不确定不能使用新的for形式,如下:
#include <iostream>
using namespace std;
int func(int a[]) {
for (auto e: a) // 编译失败,迭代范围不确定
cout << e;
}
int main() {
int arr[] = {1, 2, 3, 4, 5};
func(arr);
}
2 auto: C++中每个变量使用前必须定义从而被视为静态语言,而不像一些脚本语言变量可以这样用x=1称为动态语言。 静态语言和动态语言主要区别:静态语言类型检查发生在编译阶段,动态语言类型检查发生在运行阶段。C++11中实现了auto和decltype从静态语言向动态语言拉近了一点。注意的是auto不再是存储类型的标识符,还记得曾几何C语言中大讲atuo、static等等变量的区别,现在auto在C++11中是个类型指示符,原来C++98的存储类型标识符语义被遗弃。auto告诉编译器所声明的变量类型在编译时期推导而得,相当于atuo是个占位符,等编译时确定变量的类型。如:atuo
x=1; 编译时x的类型自动推断为int,其等价于int x=1;
一个实用之处就是std::vector<int>::iterator it=vec.begin();现在可以写为: auto it=vec.begin()
atuo增加了类型的灵活性,比如:
class PI { public: double operator* (float v) { return (double)val * v; // 这里精度被扩展了 } const float val = 3.1415927f; }; int main() { float radius = 1.7e10; PI pi; auto circumference = 2 * (pi * radius);//atuo的一个好处就是,如果将来PI中的operator* 返回值类型改变了,main中不需要跟着修改 }atuo不能解决溢出问题,如:
unsigned int a=4294967295;//最大无符号整数 unsigned int b=1; auto c=a+b;//c=0atuo为跨平台带来了一定好处,如:
auto var=strlen("hello world")//strlen在32位下返回4字节,64位下返回8字节整型,实用auto完全屏蔽了该细节auto提高了模板的灵活性:
template<typename T1, typename T2> double Sum(T1 & t1, T2 & t2) {//遗憾的是这里double不能用auto了,编译器报错 auto s = t1 + t2; // s的类型会在模板实例化时被推导出来,不过此处已经增加了灵活性 return s; } int main() { int a = 3; long b = 5; float c = 1.0f, d = 2.3f; auto e = Sum(a, b); // s的类型被推导为long auto f = Sum(c, d); // s的类型被推导为float }auto用于宏的一个惊艳例子:
#define Max1(a, b) ((a) > (b)) ? (a) : (b)//遇见一个宏替换a或b就要计算一次a或b #define Max2(a, b) ({ \ auto _a = (a); \ //这里将宏替换a的计算结果先保存至_a中,这样减少了计算开销 auto _b = (b); \ (_a > _b) ? _a: _b; }) int main() { int m1 = Max1(1*2*3*4, 5+6+7+8); int m2 = Max2(1*2*3*4, 5+6+7+8);//计算开销减小 }atuo与指针和引用的关系,如:
int x; int * y = &x; double foo(); int & bar(); auto * a = &x; // int*,这里auto* 和auto一样的 auto & b = x; // int&,这里必须是atuo& auto c = y; // int* auto * d = y; // int* auto * e = &foo(); // 编译失败, 指针不能指向一个临时变量 auto & f = foo(); // 编译失败, nonconst的左值引用不能和一个临时变量绑定 auto g = bar(); // int auto & h = bar(); // int&CV限制符:auto, volatile, const 分别表示类型标示符,易失的,常量的。
double foo(); float * bar(); const auto a = foo(); // a: const double const auto & b = foo(); // b: const double& volatile auto * c = bar(); // c: volatile float* auto d = a; // d: double, 简单的auto并不能带走右边的cv特性 auto & e = a; // e: const double &,但是auto&能带走右边的cv特性 auto f = c; // f: float * volatile auto & g = c; // g: volatile float * &auto可以和初始化列表和new一起使用:
#include <initializer_list> auto x = 1; auto x1(1); auto y {1}; // 使用初始化列表的auto auto z = new auto(1); // 可以用于new
auto在同一个语句中推断多个变量,第一个atuo的类型就是后面变量的了性,从左至右推导,如:
auto a=1,b=2;//等价于int a=1; int b=2;但是建议不要这样使用auto,还是每个auto推断单独占一行一些auto不适用的地方:
#include <vector> using namespace std; void fun(auto x =1){} // 1: auto函数参数,无法通过编译 struct str{ auto var = 10; // 2: auto非静态成员变量,无法通过编译 }; int main() { char x[3]; auto y = x; auto z[3] = x; // 3: auto数组,无法通过编译 // 4: auto模板参数(实例化时),无法通过编译 vector<auto> x = {1}; }
3 decltype:类型推导
typeid(类名).name()可以返回类型的名字,typeid(类名).hash_code()返回类型对应的唯一hash值,该hash值可以比较,如:typeid(类1).hash_code()==typeid(类名2).hash_code()。is_same<类名1,类名2>::value也可以判断两个类是否相同。
int i; decltype(i) j = 0; cout << typeid(j).name() << endl; // 打印出"i", g++表示integer float a; double b; decltype(a + b) c; cout << typeid(c).name() << endl; // 打印出"d", g++表示doubledecltype不像auto那样从变量声明的初始化表达式获得变量的类型,decltype总是以一个普通的表达式为参数,返回该表达式的类型。decltype可以获得的类型来定义另外一个变量,与auto相同的是,decltype也是在编译是进行的。
decltype和using结合的例子:
using size_t=decltype(sizeof(0));//size_t就可以跨平台使用了decltype可以像auto it=vec.begin()一样用于标准容器:
vector<int> vec; typedef decltype(vec.begin()) vectype; vectype i; // 这是auto无法做到的 for (i = vec.begin(); i < vec.end(); i++) { // 做一些事情 } for (decltype(vec)::iterator i = vec.begin(); i < vec.end(); i++) { // 做一些事情 }decltype可以重用匿名类型:
enum class{K1, K2, K3}anon_e; // 匿名的强类型枚举 union { decltype(anon_e) key; char* name; }anon_u; // 匿名的union struct { int d; decltype(anon_u) id; }anon_s[100]; // 匿名的struct数组 int main() { decltype(anon_s) as;//重用匿名类型的类型 as[0].id.key = decltype(anon_e)::K1; // 引用匿名强类型枚举中的值 }//通过匿名的变量名anon_e等加上decltype可以推断这些匿名类型的类型并进行重用,不过这个似乎违背了匿名类型的设计初衷decltype重塑前面“auto提高了模板的灵活性”的例子:
// s的类型被声明为decltype(t1 + t2) template<typename T1, typename T2> void Sum(T1 & t1, T2 & t2, decltype(t1 + t2) & s) {//可以通过参数引用s返回这个不确定类型的返回值 s = t1 + t2; } int main() { int a = 3; long b = 5; float c = 1.0f, d = 2.3f; long e;//这里仍然需要定义e的类型,传给Sum,还不是完全智能的,后面的函数返回值推导可以解决这个问题 float f; Sum(a, b, e); // s的类型被推导为long Sum(c, d, f); // s的类型被推导为float }decltype用于实例化模板:
int hash(char*);//一个hash函数 //map<char*, decltype(hash)> dict_key; // 无法通过编译,hash是个函数名,hash(nullptr)才是表达式满足decltype map<char*, decltype(hash(nullptr))> dict_key;模板类result_of实现就是基于decltype的,用于推断函数的返回值类型,位于头文件<type_traits>:
typedef double (*func)(); result_of<func()>::type f; // 由func()推导其结果类型,doubledecltype(e)推断的四个规则:
1) 当e是一个没有带括号的标记符表达式(除去关键字、字面量等编译器需要使用的标记之外的程序员自定义的标记)或者类成员访问表达式,那么decltype(e)就是e所命名的实体的类型。如果e是一个被重载的函数名,则会编译错误。
2) 否则,假设e类型是T,若e是一个将亡值(如函数非引用返回值),那么decltype(e)为T&&
3) 否则,若e是一个左值,decltype(e)为T&,这是一个左值规则
4) 否则,decltype(e)为T
例子如下:
int i = 4; int arr[5] = {0}; int *ptr = arr; struct S { double d; } s; void Overloaded(int); void Overloaded(char); // 重载的函数 int && RvalRef(); const bool Func(int); // 规则1: 单个标记符表达式以及访问类成员,推导为本类型 decltype(arr) var1; // int[5], 标记符表达式 decltype(ptr) var2; // int*, 标记符表达式 decltype(s.d) var4; // double, 成员访问表达式 decltype(Overloaded) var5; // 无法通过编译,是个重载的函数 // 规则2: 将亡值,推导为类型的右值引用 decltype(RvalRef()) var6 = 1; // int&& // 规则3: 左值,推导为类型的引用 decltype(true ? i : i) var7 = i; // int&, 三元运算符,这里返回一个i的左值 decltype((i)) var8 = i; // int&, 带圆括号的左值 decltype(++i) var9 = i; // int&, ++i返回i的左值 decltype(arr[3]) var10 = i; // int& []操作返回左值 decltype(*ptr) var11 = i; // int& *操作返回左值 decltype("lval") var12 = "lval"; // const char(&)[9], 字符串字面常量为左值 // 规则4:以上都不是,推导为本类型 decltype(1) var13; // int, 除字符串外字面常量为右值 decltype(i++) var14; // int, i++返回右值 decltype((Func(1))) var15; // const bool, 圆括号可以忽略规则3左值规则,确实比较难以判断,可以通过一定技术检查,先声明decltype定义的变量,再在其它语句对其初始化,若是左值必须声明即初始化,那么编译器会报错。也可以借助is_lvalue_reference等模板类判断变量引用类型:
#include <type_traits> #include <iostream> using namespace std; int i = 4; int arr[5] = {0}; int *ptr = arr; int && RvalRef(); int main(){ cout << is_rvalue_reference<decltype(RvalRef())>::value << endl; // 1 cout << is_lvalue_reference<decltype(true ? i : i)>::value << endl; // 1 cout << is_lvalue_reference<decltype((i))>::value << endl; // 1 cout << is_lvalue_reference<decltype(++i)>::value << endl; // 1 cout << is_lvalue_reference<decltype(arr[3])>::value << endl; // 1 cout << is_lvalue_reference<decltype(*ptr)>::value << endl; // 1 cout << is_lvalue_reference<decltype("lval")>::value << endl; // 1 cout << is_lvalue_reference<decltype(i++)>::value << endl; // 0 cout << is_rvalue_reference<decltype(i++)>::value << endl; // 0 }decltype可以带来冗余的符号,如decltype(T) & a中decltype本身可能就推断出是个引用,后面由出现一个&则出现冗余的&。auto不能带走初始化表达式的cv限制符,但是decltype却能带走其表达式的cv限制符。
#include <type_traits> #include <iostream> using namespace std; const int ic = 0; volatile int iv; struct S { int i; }; const S a = {0}; volatile S b; volatile S* p = &b; int main() { cout << is_const<decltype(ic)>::value << endl; // 1 cout << is_volatile<decltype(iv)>::value << endl; // 1 cout << is_const<decltype(a)>::value << endl; // 1 cout << is_volatile<decltype(b)>::value << endl; // 1 cout << is_const<decltype(a.i)>::value << endl; // 0, 成员不是const //成员变量失去了其所属类型的cv属性 cout << is_volatile<decltype(p->i)>::value << endl; // 0, 成员不是volatile }decltype最后的推断结果包含了一些冗余符号cv限制符和引用&则可以忽略多余的符号
#include <type_traits> #include <iostream> using namespace std; int i = 1; int & j = i; int * p = &i; const int k = 1; int main() { decltype(i) & var1 = i; decltype(j) & var2 = i; // 冗余的&, 被忽略 cout << is_lvalue_reference<decltype(var1)>::value << endl; // 1, 是左值引用 cout << is_rvalue_reference<decltype(var2)>::value << endl; // 0, 不是右值引用 cout << is_lvalue_reference<decltype(var2)>::value << endl; // 1, 只是左值引用 //decltype(p)* var3 = &i; // 无法通过编译 //auto*和auto相同,但是decltype的*不能省略,这里推断结果int** var3和右边的&i不符 decltype(p)* var3 = &p; // var3的类型是int** auto* v3 = p; // v3的类型是int* v3 = &i; const decltype(k) var4 = 1; // 冗余的const,被忽略 }
4 追踪返回类型
先看一个实例:
#include <iostream>
using namespace std;
template<typename T1, typename T2>
auto Sum(const T1 & t1, const T2 & t2) -> decltype(t1 + t2)
{//当模板的返回值类型是随着模板实例化才能确定时,通过auto和decltype追踪返回值类型,auto相当于占位符,最后模板实例化从左至右,最后decltype推导出返回值类型填写回auto处即可
return t1 + t2;
}
template <typename T1, typename T2>
auto Mul(const T1 & t1, const T2 & t2) -> decltype(t1 * t2)
{
return t1 * t2;
}
int main()
{
auto a = 3;
auto b = 4L;
auto pi = 3.14;
auto c = Mul(Sum(a, b), pi);//完全根据模板实例化自动推导参数
cout << c << endl; // 21.98
}
追踪返回类型用于简化函数定义:
#include <type_traits>
#include <iostream>
using namespace std;
// 有的时候,你会发现这是面试题
int (*(*pf())())() {//这个看起来真的比较蛋疼
return nullptr;
}
// auto (*)() -> int(*) () 一个返回函数指针的函数(假设为a函数)
// auto pf1() -> auto (*)() -> int (*)() 一个返回a函数的指针的函数
auto pf1() -> auto (*)() -> int (*)() {//从右向左解析
return nullptr;
}
int main() {
cout << is_same<decltype(pf), decltype(pf1)>::value << endl; // 1
}
追踪返回值类型用于转发函数中:
#include <iostream> using namespace std; double foo(int a) { return (double)a + 0.1; } int foo(double b) { return (int)b; } template <class T> auto Forward(T t) -> decltype(foo(t)){//转发函数 return foo(t); } int main(){ cout << Forward(2) << endl; // 2.1 cout << Forward(0.5) << endl; // 0 }
5 for循环
标准库已经有了for_each
for_each( InputIt first, InputIt last, UnaryFunction f );c++11有了新的for循环方式:
#include <vector>
#include <iostream>
using namespace std;
int main() {
vector<int> v = {1, 2, 3, 4, 5};
for (auto i = v.begin(); i != v.end(); ++i)
cout << *i << endl; // i是迭代器对象
for (auto e: v)//冒号分开,前面部分是范围内用于迭代的变量,后半部分是迭代的范围,只能用于明确已知迭代范围
cout << e << endl; // e是解引用后的对象
}
对于迭代范围不确定不能使用新的for形式,如下:
#include <iostream>
using namespace std;
int func(int a[]) {
for (auto e: a) // 编译失败,迭代范围不确定
cout << e;
}
int main() {
int arr[] = {1, 2, 3, 4, 5};
func(arr);
}
相关文章推荐
- 新兵易学,老兵易用----C++(C++11的学习整理---如何减少代码量,加强代码的可读性)
- C++11常用特性学习-易学易用新特性(>>/auto/decltype/范围的for)
- 新手易学——新手学淘宝开店
- 妙味课堂史上最全的javascript视频教程,前端开发人员必备知识点,新手易学,拔高必备!!!
- Bootstrap 栅格系统 学习总结 Bootstrap框架是如今最流行的前端框架之一,Bootstrap功能强大,简单易学,很符合实际应用场景。 只是Bootstrap的内容较多,新手
- 新手易学黑客攻防入门
- angular初体验(简单易学,适合新手,很新很新的手哈哈哈)
- 基于Laravel框架的一个简单易学的微信商城(新手必学)
- 关于新手晋级程序员
- 【程序员的自我修养】写给新手程序员的一封信
- Mac新手操作指南(一)
- 新手随笔
- Django新手教程
- 新手网上开店赚钱的几大注意事项
- 推荐C++新手使用Ubuntu下Geany+Scribes C++环境
- Jquery mobile 新手问题总汇
- 软件包管理 之 Fedora/Redhat 在线安装更新软件包,yum 篇 ── 给新手指南
- C++11 std::move语意经典理解
- 新手注册
- windows下eclipse+CDT新手配置过程