4-1.右尖括号>的改进,C++98将>>优先解析为右移,C++11取消此种限制
template <int i> class X{};
template <class T> class Y{};
Y<X<1> > x1;
Y<X<2>> x2;
/*
编译选项:g++ -c 4-1-1.cpp std=c++98
g++ -c 4-1-1.cpp std=c++11
代码清单: 4-1
代码功能:右尖括号>的改进,C++98将>>优先解析为右移,C++11取消此种限制
*/
4-2.C++11会将>>解析为模板参数界定符
template <int i> class X{};
template <class T> class Y{};
Y<X<1> > x1;
Y<X<2>> x2;
/*
编译选项:g++ -c 4-1-1.cpp std=c++98
g++ -c 4-1-1.cpp std=c++11
代码清单: 4-1
代码功能:右尖括号>的改进,C++98将>>优先解析为右移,C++11取消此种限制
*/
template <int i> class X {};
X<(1 >> 5)> x;
/*
编译选项:g++ -c 4-1-2-2.cpp std=c++98 可以编译通过,98编译器认为中间的双尖括号是一个位移操作
g++ -c 4-1-2-2.cpp std=c++11 可以编译通过,()修正了编译器带来的误解
代码清单: 4-1
代码功能:右尖括号>的改进,C++98将>>优先解析为右移,C++11取消此种限制
*/
4-3.用auto关键字来要求编译器变简单变量name的类型进行自动推导
#include <iostream>
using namespace std;
int main(){
auto name = "world.\n";
cout << "hello," << name;
}
template <int i> class X {};
X<(1 >> 5)> x;
/*
编译选项:g++ -c 4-2-1.cpp std=c++98
g++ -c 4-2-1.cpp std=c++11
g++ -c 4-2-1.cpp
代码清单: 4-2-1
代码功能:用auto关键字来要求编译器变简单变量name的类型进行自动推导
*/
4-4.展示auto类型推导的基本用法
int main(){
double foo();
auto x = 1; // x的类型为int
auto y = foo(); // y的类型为double
struct m {int i ;}str;
auto str1 = str; // str1的类型为struct m
auto z; // 无法推导,无法通过编译
z = x;
}
4-5.超长的迭代器使用回顾
#include <vector>
#include <string>
void loopover(std::vector<std::string> & vs){
std::vector<std::string>::iterator i = vs.begin();
for(; i<vs.end(); i++)
{
}
}
/*
编译选项:g++ -c 4-2-3.cpp -c
代码清单: 4-5
代码功能:超长的迭代器使用回顾
*/
4-6.用auto来代替4-5例子中的超长迭代器(用起来爽,复杂工程的后期阅读可能会有障碍)
#include <vector>
#include <string>
void loopover(std::vector<std::string> & vs){
for(auto i = vs.begin(); i<vs.end(); i++)
{
}
}
/*
编译选项:g++ -c 4-2-4.cpp -c -std=c++11
代码清单: 4-6
代码功能:超长的迭代器使用回顾
*/
4-7.auto有时可以为我们解决一些精度问题
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);
}
/*
编译选项:g++ -c 4-2-5.cpp -c -std=c++11
代码清单: 4-7
代码功能:auto有时可以为我们解决一些精度问题
*/
4-8.auto并不能解决所有的精度问题
#include <iostream>
using namespace std;
int main(){
unsigned int a = 4294967295; // 最大的unsigned int的值
unsigned int b = 1;
auto c = a + b; // c的类型依然是unsigned int
cout << "a = "<< a << endl; // a = 4294967295
cout << "b = "<< b << endl; // b = 1
cout << "a +b = "<< c << endl; // a +b = 0
return 0;
}
/*
编译选项:g++ -c 4-2-6.cpp -c -std=c++11
代码清单: 4-8
代码功能:auto并不能解决所有的精度问题
*/
4-9.模板定义中的auto的“自适应性”得到充分体现
4-15.语法的二义性和实现的困难性使得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数组,无法通过编译
vector<auto> v = {1}; // 4:auto模板参数实例化时,无法通过编译
return 0;
}
/*
编译选项:g++ -std=c++11 4-2-13.cpp -c
代码清单: 4-15
代码功能:语法的二义性和实现的困难性使得auto也有限制
*/
4-16.用typeid类型中的方法查询变量的类型,名字及判断类型是否相同
#include <iostream>
#include <typeinfo>
using namespace std;
class White{};
class Black{};
int main(){
White a;
Black b;
cout<< typeid(a).name()<<endl;
cout<< typeid(b).name()<<endl;
White c;
bool a_b_sametype = (typeid(a).hash_code() == typeid(b).hash_code());
bool a_c_sametype = (typeid(a).hash_code() == typeid(c).hash_code());
cout<<"Same type"<<endl;
cout<<"A and B ? "<<(int)a_b_sametype<<endl;
cout<<"A and C ? "<<(int)a_c_sametype<<endl;
return 0;
}
/*
编译选项:g++ -std=c++11 4-3-1.cpp
代码清单: 4-16
代码功能:用typeid类型中的方法查询变量的类型,名字及判断类型是否相同
*/
4-17.decltype的使用方法的简单示例
#include <iostream>
#include <typeinfo>
using namespace std;
int main(){
int i;
decltype(i) j = 0;
cout << typeid(j).name() << endl; // 打印出来“i”,g++表示int
float a;
double b;
decltype(a+b) c;
auto d = a+b;
cout << typeid(c).name() << endl; // 打印出来“d”,g++表示double
cout << typeid(d).name() << endl; // 打印出来“d”,g++表示double
return 0;
}
/*
编译选项:g++ -std=c++11 4-3-2.cpp
代码清单: 4-17
代码功能:
*/
4-18.decltype的使用方法的简单示例2
#include <vector>
#include <iostream>
using namespace std;
int main(){
vector<int> vec{1,2,3};
typedef decltype(vec.begin()) vectype;
for(vectype i = vec.begin();i<vec.end();i++){
cout<<"test line 1"<<endl;
}
for(decltype(vec)::iterator i = vec.begin();i<vec.end();i++){
cout<<"test line 2"<<endl;
}
return 0;
}
4-19.书上说decltype可以根据匿名类型的变量名推导类型并进行重用,但我的编译器没有编过
enum class{K1,K2,K3}anon_e; // 匿名的强类型枚举
union{
decltype(anon_e) key;
char* name;
}anon_u; // 匿名的union联合体,C++11编译器会报错啊
struct {
int d;
decltype(anon_u) id;
}anon_s[1000]; // 匿名的struct数组
int main(){
decltype(anon_s) as;
as[0].id.key = decltype(anon_e)::K1;// 引用匿名强类型枚举中的值
}
/*
编译选项:g++ -std=c++11 4-3-4.cpp 编译报错了,后面再看看
代码清单: 4-19
代码功能:书上说decltype可以根据匿名类型的变量名推导类型并进行重用,但我的编译器没有编过
*/
4-20.decltype使得代码清单4-9中的Sum的使用范围增加
template<typename T1,typename T2>
void Sum(T1 & t1,T2 & t2,decltype(t1+t2) & s)// s的类型被声明为decltype(t1+t2)
{
s = t1 + t2;
}
int main(){
int a = 3;
long b = 5;
float c = 1.0f,d = 2.3f;
long e;
float f;
Sum(a,b,e); // s的类型被推导为long
Sum(c,d,f); // s的类型被推导为float
}
/*
编译选项:g++ -std=c++11 4-3-5.cpp
代码清单: 4-20
代码功能:decltype使得代码清单4-9中的Sum的使用范围增加
*/
4.21.decltype可以使得两个数组得以在模板实例化时得到匹配
#include <iostream>
using namespace std;
template<typename T1,typename T2>
void Sum(T1 & t1,T2 & t2,decltype(t1 + t2)& s){
s = t1 + t2;
cout << "模板的实例化版本" <<endl;
}
void Sum(int a[],int b[],int c[]){
cout << "数组版本" <<endl;
}
int main(){
int a[5],b[10],c[5];
Sum(a,b,c); // 数组版本
int d,e,f;
Sum(d,e,f); // 模板的实例化版本
}
/*
编译选项:g++ -std=c++11 4-3-6.cpp
代码清单: 4-21
代码功能:decltype可以使得两个数组得以在模板实例化时得到匹配
*/
4-22.decltype可以使得两个数组得以在模板实例化时得到匹配(我的ubuntu都编译不过)
#include <map>
using namespace std;
int hash(char*){};
map<char*,decltype(hash)> dict_key;
map<char*,decltype(hash(nullptr))> dict_key1;
/*
编译选项:g++ -c -std=c++11 4-3-7.cpp
代码清单: 4-22
代码功能:decltype可以使得两个数组得以在模板实例化时得到匹配(我的ubuntu都编译不过)
*/
4-23.基于decltype的模板类result_of推导函数的返回类型的示例
#include <type_traits>
using namespace std;
typedef double (*func)();
int main(){
result_of<func()>::type f;
}
/*
编译选项:g++ -c -std=c++11 4-3-8.cpp
代码清单: 4-23
代码功能:基于decltype的模板类result_of推导函数的返回类型的示例
*/
4-24.基于decltype的使用小陷阱
int i;
decltype(i) a; // a:int
decltype((i)) b; // b:int &,无法编译通过
/*
编译选项:g++ -c -std=c++11 4-3-9.cpp
代码清单: 4-24
代码功能:基于decltype的使用小陷阱
*/
4-25.一个例子加深对decltype的使用的理解
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) val1; // int[5],标记符表达式
decltype(ptr) val2; // int*,标记符表达式
decltype(s.d) val4; // double,成员访问表达式
decltype(Overloaded) var5; // 无法通过编译,是个重载的函数
// 规则2:将亡值,推导为类型的右值引用
decltype(RvalRef()) var6 = 1;
// 规则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) val11 = i; // int& *操作返回左值
decltype("lval") val12 = "lval"; // const char(&) [9],字符串字面常量为左值
// 规则4:以上都不到,推导为本类型
decltype(1) var13; // int,除字符串外字面常量为右值
decltype(i++) var14; // int,i++返回右值
decltype((Func(1))) var15; // const bool,圆括号可以忽略
/*
编译选项:g++ -c -std=c++11 4-3-10.cpp
代码清单: 4-25
代码功能:一个例子加深对decltype的使用的理解
*/
4-26.用is_lvalue_reference/is_rvalue_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
return 0;
}
/*
编译选项:g++ -std=c++11 4-3-11.cpp
代码清单: 4-26
代码功能:用is_lvalue_reference/is_rvalue_reference帮助我们判断是否推导出左右值引用
*/
4-27.decltype可以带走表达式的cv限制符,但成员不会继承对象定义中的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
cout << is_volatile<decltype(p->i)::value> << endl; // 0 成员不是volatile
}
/*
编译选项:g++ -std=c++11 4-3-12.cpp
代码清单: 4-27
代码功能:decltype可以带走表达式的cv限制符,但成员不会继承对象定义中的cv限制符
*/
4-28.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; // 无法通过编译,在decltype进行推导的时候*不会被忽略
decltype(p)* var3 = &p; // var3的类型是int**
auto* v3 = p; // v3的类型是int*
v3 = &i;
const decltype(k) var4 = 1; // 冗余的const被忽略
}
/*
编译选项:g++ -std=c++11 4-3-13.cpp
代码清单: 4-28
代码功能:decltype从表达式推导出类型后进行定义时,也会允许一些冗余的符号,如cv限制及&符号
*/
4-29.使用追踪返回类型的函数的意外之喜(简洁)
class OuterType{
struct InnerType{int i;};
InnerType GetInner();
InnerType it;
};
// 可以不必写成OuterType::GetInner()->OuterType::InnerType
auto OuterType::GetInner()->InnerType{
return it;
}
/*
编译选项:g++ -std=c++11 4-4-1.cpp -c
代码清单: 4-29
代码功能:使用追踪返回类型的函数的意外之喜(简洁)
*/
4-30.使用追踪返回类型的函数带来的类型推导下的泛型编程的一个例子
#include <iostream>
using namespace std;
template<typename T1,typename T2>
auto Sum(const T1& t1,const T2& t2)->decltype(t1 + t2){
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
return 0;
}
/*
编译选项:g++ -std=c++11 4-4-2.cpp
代码清单: 4-30
代码功能:使用追踪返回类型的函数带来的类型推导下的泛型编程的一个例子
*/
4-31.用传统方法和跟踪返回类型的方法定义一个返回函数指针(该函数指针指向一个返回函数指针的函数)的函数
#include <type_traits>
#include <iostream>
using namespace std;
int ( *(*pf())() ) (){
return nullptr;
}
// auto (*) () ->int(*)() 一个返回函数指针的函数(假设为a函数)
// auto pf1() ->int(*)()->int(*)() 一个返回a函数的指针的函数
auto pf1()->auto(*)()->int(*)(){
return nullptr;
}
int main(){
cout << is_same<decltype(pf),decltype(pf1)>::value<<endl; // 1
}
/*
编译选项:g++ -std=c++11 4-4-3.cpp
代码清单: 4-31
代码功能:用传统方法和跟踪返回类型的方法定义一个返回函数指针(该函数指针指向一个返回函数指针的函数)的函数
*/
4-32.使用追踪返回类型可以实现参数和返回类型不同的时候的转发
#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
return 0;
}
/*
编译选项:g++ -std=c++11 4-4-4.cpp
代码清单: 4-32
代码功能:使用追踪返回类型可以实现参数和返回类型不同的时候的转发
*/
4-33.使用指针遍历循环打印元素值
#include <iostream>
using namespace std;
int main(){
int arr[5] = {1,2,3,4,5};
int * p;
for(p = arr;p < arr + sizeof(arr)/sizeof(arr[0]);++p){
*p *=2;
}
for(p = arr;p < arr + sizeof(arr)/sizeof(arr[0]);++p){
cout << *p << '\t';
}
cout<<endl;
}
/*
编译选项:g++ -std=c++11 4-5-1.cpp
代码清单: 4-33
代码功能:使用指针遍历循环打印元素值
*/
4-34.使用for_each循环打印元素值
#include <algorithm>
#include <iostream>
using namespace std;
int action1(int & e){e*=2;}
int action2(int & e){cout << e << '\t';}
int main(){
int arr[5]={1,2,3,4,5};
for_each(arr,arr+sizeof(arr)/sizeof(arr[0]),action1);
for_each(arr,arr+sizeof(arr)/sizeof(arr[0]),action2);
cout <<endl;
return 0;
}
/*
编译选项:g++ -std=c++11 4-5-2.cpp
代码清单: 4-34
代码功能:使用for_each循环打印元素值
*/
4-35.基于范围的for循环改写打印元素值
#include <algorithm>
#include <iostream>
using namespace std;
int main(){
int arr[5]={1,2,3,4,5};
for(int &e:arr){
e* = 2;
}
for(int &e:arr){
cout << e << '\t';
}
cout <<endl;
return 0;
}
/*
编译选项:g++ -std=c++11 4-5-3.cpp
代码清单: 4-35
代码功能:基于范围的for循环改写打印元素值
*/
4-36.展示错误代码:使用基于范围循环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);
}
/*
编译选项:g++ -std=c++11 4-5-4.cpp
代码清单: 4-36
代码功能:展示错误代码:使用基于范围循环for循环迭代的时候传递的范围应该是确定的
*/
4-37.用不同的auto方法遍历vector<int>对象
#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 f:v)
cout << f << endl; // f是解引用后的对象
}
/*
编译选项:g++ -std=c++11 4-5-5.cpp
代码清单: 4-37
代码功能:用不同的auto方法遍历vector<int>对象
*/