【深入理解C++11:C++新特性解析】第4章 Newer易学 Older易用 测试代码整理

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>对象
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值