auto decltype 用于返回值类型后置时的占位

decltype是 GCC 实现的第一个 C++ 11 新特性(在C++程序设计语言中,decltype作为操作符,用于获取表达式的数据类型。 C++11标准引入decltype,主要是为泛型编程而设计,以解决泛型编程中有些类型由模板参数决定而难以(甚至不可能)表示的问题)。它实际上起源于一个相当古老的 GNU 扩展关键字——__typeof__。这个非标准关键字也能够在 C 语言中使用,GNU Compiler Collection 的专业用户可能对它更熟悉一些。2008 年,GCC 4.3.x 就实现了这个特性,同时去除了__typeof__的一些缺点。现在,decltype和__decltype两个关键字在 GCC 中都适用;前者只能用在 C++ 11 模式下,后者可以同时应用于 C++ 11 和 C++ 98 模式。__typeof__则已经停止使用。

   decltype的基本使用。简单来说,decltype关键字用于查询表达式的类型。不过,这只是其基本用法。当这个简单的表述同 C++ 11 的其它特性结合起来之后,一些意想不到的有趣用法就此产生。

   表达式如下:

  1. decltype ( expression )  
简单使用如下:
  1. const int&& foo();  
  2.  int i; struct A { double x; };   
  3. const A* a = new A();   
  4. decltype(foo())  x1;  // const int&&  
  5. decltype(i)      x2;  // int            
  6. decltype(a->x)   x3;  // double            
  7. decltype((a->x)) x4;  // double&   
传统的__typeof__有一个颇为诟病的地方,在于不能很好地处理引用类型。而decltype则没有这个问题。而decltype实际上更好地融入了 C++ 11 类型系统,来看一个比较复杂的例子:
  1. int i; float f; double d;   
  2. typedef decltype(i + f) type1;  // float   
  3. typedef decltype(f + d) type2;  // double   
  4. typedef decltype(f < d) type3;  // bool  

   上面的例子清楚看出,decltype 能够很好地处理类型转换这里问题。
或许你会对上面代码中的 (4) 心生疑问。为什么decltype((a->x))会是double&?这是由 decltype 的定义决定的。decltype 判别的规律还是比较复杂的:
对于decltype( e )而言,其判别结果受以下条件的影响:
如果e是一个标识符或者类成员的访问表达式,则decltype(e)就是e所代表的实体的类型。如果没有这种类型或者e是一个重载函数集,那么程序是错误的(上例中的 (2) 和 (3));
如果e是一个函数调用或者一个重载操作符调用(忽略e外面的括号),那么decltype(e)就是该函数的返回类型(上例中的 (1));
如果e不属于以上所述的情况,则假设e的类型是 T:当e是一个左值时,decltype(e)就是T&;否则(e是一个右值),decltype(e)是T(上例中的 (4) 即属于这种情况。在这个例子中,e实际是(a->x),由于有这个括号,因此它不属于前面两种情况,所以应当以本条作为判别依据。而(a->x)是一个左值,因此会返回double &)。

说了这么多,decltype到底有什么用?事实上,decltype在复杂的模板编程中非常有用。不过,这需要结合我们前面提到的auto关键字。举个经典的例子,请看下面的代码:

最后,我们来看一个更加实际的例子。在 GCC 的 C++11 运行时库中有这么一段代码:

  1. template<typename _IteratorL, typename _IteratorR>  
  2. inline auto operator-(const reverse_iterator<_IteratorL>& __x,  
  3.                       const reverse_iterator<_IteratorR>& __y)  
  4.     -> decltype(__y.base() - __x.base())  
  5. {  
  6.     return __y.base() - __x.base();  
  7. }  
现在,这段代码应该更加清晰了。这实际上解决了 C++ 98 实现的一个真正的 bug。在 GCC 的 C++ 98 版本中,这段代码是这样的:
  1. template<typename _IteratorL, typename _IteratorR>  
  2. inline typename reverse_iterator<_IteratorL>::difference_type operator-(  
  3.           const reverse_iterator<_IteratorL>& __x,  
  4.           const reverse_iterator<_IteratorR>& __y)  
  5. {  
  6.     return __y.base() - __x.base();  
  7. }  
这段代码只有在这两方的difference_type相同时才适用

简洁用法如下:

  1. template <class T, class U>  
  2.  auto Multiply(T t, U u)->decltype(t*u)  
  3. {  
  4.  typedef decltype(t*u) NewType;  
  5.  NewType *pResult = new NewType(t*u);  
  6.  return *pResult;  
  7. }  

至于为什么需要将返回值类型后置,这里简单说明一下。如果没有后置,则函数声明为decltype(t*u) Multiply(T t, U u),但此时模板参数t和u还未声明,编译无法通 过。另外,如果非要使用返回值类型前置的形式,也可以将函数声明为decltype((*(T *)0)*(*(U *)0)) Multiply(T t, U u),但这种形式比较晦涩难懂,因此不推荐采用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值