C++11学习笔记4——简化代码书写

       C++11中的一些新特性,用来简化代码书写,提升代码效率。

1、右尖括号 > 的改进

      在C++98中,编译器会优先将 >> 解析为右移符号,因此,如果在实例化模板时出现连续两个<符号时,它们之间应该用一个空格来进行分割,避免编译错误。

      在C++11中,上述问题不再存在,C++11标准要求编译器智能地去判断哪些情况下 >> 不是右移符号。例如:

template <int i> class X{};
template <class T> class Y{};

Y<X<1> > x1;  //C++98成功, C++11成功
Y<X<2>>  x2;  //C++98失败, C++11成功

      注意:如果真的出现右移符号与连续的>>的冲突(比如模板中确实想使用右移符号),可以通过使用园括号来保证右移操作优先;例如:X<(1 >> 5)> x;


2、auto类型推导

      在C++11中auto不再是一个存储类型指示符(storage-class-specifier,如static、extern、thread_local等),而是作为一个新的类型指示符(type-specifier,如int\float等)来指示编译器,auto声明变量的类型必须由编译时期推导而得。

      volatile和const代表了变量的两种不同属性:易失性和常量性。在C++标准中,它们常常被一起叫作cv限制符(cv-qualifier )。C++11标准规定auto可以与cv限制符一起使用,不过声明为auto的变量并不能从其初始化表达式中“带走”cv限制符,例如:

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 & e = a;               //e:  const double &
auto f = c;                 //f: float *
volatile auto & g = c;      //g: volative float * &
       用auto来声明多个变量类型时,只有第一个变量用于auto的类型推导,然后推导出来的数据类型被作用于其他的变量,例如
auto x = 1, y = 2;  //x和y的类型均为int

// m是一个指向const int类型变量的指针,n是一个int类型的变量
const auto* m = &x, n = 1;

auto i = 1, j =3.14f;  //编译失败
auto o = 1, &p = o, *q = &p; //o: int, p是o的引用,q是p的指针
        C++11新引入的初始化列表,以及new,都可以使用auto关键字,例如:

#include <initializer_list>

auto x = 1;
auto x1( 1 );

auto y {1}; //使用初始化列表的auto
auto z = new auto(1);  //可以用于new

       注意:auto使用上也有限制,以下四种情况不能推导:

       ** auto不能是函数的形参;

       ** auto不能是结构体的非静态成员变量;

       ** 不能声明auto数组;

       ** 在实例化模板时不能使用auto作为模板参数,如vector< auto > v


3、decltype

      C++中存在RTTI用于运行时类型识别,但是它会带来一些运行时的开销,一些编译器会让用户选择性地关闭该特性(如XL C/C++编译器的 -qnortti, GCC的-fno-rttion ,微软的 /GR- )。

       与RTTI对应,decltype则允许编译时的类型推导。

       注意:decltype只接受表达式做参数,函数名不能作为其参数。

标准库中基于decltype实现的模板类result_of的作用是推导函数的返回类型,例如

#include <type_traits>
using namesapce std;

typedef double (*func) ();

int main()
{
      result_of<func()>::type f; //由func()推导其结果类型
}
这里f 的类型为double,而result_of并没有真正调用func()这个函数。
       
        decltype推导的四规则:decltype( e )

        ** 如果e是一个没有带括号的标记符表达式(id-expression )或者类成员访问表达式,那么decltype(e)就是e所命名的实体的类型。

        ** 否则 ,假设e的类型为T, 如果e是一个将亡值,那么decltype(e)为T&&。

        **否则,假设e的类型为T,如果e是一个左值,那么decltype(e)为T&。

        **否则,假设e的类型为T,则decltype(e)为T。

注:基本上,所有除去关键字、字面量等编译器需要使用的标记之外的程序员自定义的标记(token)都可以是标记符(identifier)。而单个标记符对应的表达式就是标记符表达式,如int arr[4]; 那么arr是一个标记符表达式,而arr[3]+0, arr[3]等则不是。

示例

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 = "lvar"; // const char (&)[9],字符串字面量为左值

//规则4:以上都不是,推导为本类型
decltype(1) var13;                //int ,除字符串外字面常量为右值
decltype( i++) var14;             //int, i++返回右值
decltype( (Func(1)) ) var15;      //const bool,圆括号可以忽略

       与auto类型推导不能“带走”cv限制符不同,decltype是能够“带走”表达式的cv限制符的。不过,如果对象的定义中有const或volatile限制符,使用decltype进行推导时,其成员不会继承const或volatile限制符。

#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
}

4、追踪返回类型

      追踪返回类型配合auto与decltype会真正释放C++11中泛型编程的能力。

     我们可以想到的最直观的返回类型推导应该如下:

template<typename T1, typename T2>
decltype(t1 +t2 ) Sum( T1 &t1, T2 &t2)
{
      return t1+t2;
}
       但是,这样写编译器在推导decltype(t1+t2)时,表达式中t1和t2都没有声明(编译器只会从左往右读入符号),按照C/C++编译器规则,变量使用前必须已经声明,因此。为了解决这个问题,C++引入了新语法——追踪返回类型,来声明和定义这样的函数。如下:

template<typename T1, typename T2>
auto Sum( T1 &t1, T2 &t2) -> decltype( t1 +t2 )
{
      return t1 + t2;
}

auto占位符和 ->return_type是构成追踪返回类型函数的两个基本元素。

       追踪返回类型用于赶回函数指针和函数引用的情况:

auto (*fp)() -> int;  //等价于 int (*fp)();
auto (&fr)() -> int;  //等价于 int (&fr)();


5、基于范围的for循环

       C++98中 的for循环如下:

#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';
      }
}
        用C++标准库中的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);
}
        C++11中基于范围的for循环如下:

#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';
}
        注意:要使用基于范围的for循环,必须依赖如下条件:

        ** for循环迭代的范围是可确定的,对于类来说,如果该类有begin和end函数,那么begin和end之间就是for循环迭代的范围;

        ** 基于范围的for循环还要求迭代的对象实现++和==等操作符。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值