C++11 新特性:decltype

decltype 是

GCC 实现的第一个C++ 11 新特性。它实际上起源于一个相当古老的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 的其它特性结合起来之后,一些意想不到的有趣用法就此产生。

decltype 的语法是

decltype ( expression )

这里的括号是必不可少的。根据前面的说法,decltype 的作用是“查询表达式的类型”,因此,上面语句的效果是,返回expression 表达式的类型。注意,decltype 仅仅“查询”表达式的类型,并不会对表达式进行“求值”。

先看一个最基础的例子:

const int&& foo();

int i;

struct A {

   double x;

};

const A* a = new A();


decltype(foo())  x1;     // const int&&      (1)

decltype(i)      x2;    // int              (2)

decltype(a->x)   x3;    // double           (3)

decltype((a->x)) x4;    // double&          (4)

传统的__typeof__ 有一个颇为诟病的地方,在于不能很好地处理引用类型。而decltype 则没有这个问题。而decltype 实际上更好地融入了C++ 11 类型系统,来看一个比较复杂的例子:

int    i;

float  f;

double d;


typedef decltype(i + f)  type1;    // float

typedef decltype(f + d)  type2;    // double

typedef decltype(f < d)  type3;    // bool

上面的例子清楚看出,decltype 能够很好地处理类型转换这里问题。或许你会对上面代码中的

(4) 心生疑问。为什么decltype((a->x)) 会是double&?这是由decltype 的定义决定的。decltype 判别的规律还是比较复杂的:

对于decltype( e ) 而言,其判别结果受以下条件的影响:

1.

如果e 是一个标识符或者类成员的访问表达式,则decltype(e) 就是e 所代表的实体的类型。如果没有这种类型或者e 是一个重载函数集,那么程序是错误的(上例中的(2) 和(3));

2.

如果e 是一个函数调用或者一个重载操作符调用(忽略e 外面的括号),那么decltype(e) 就是该函数的返回类型(上例中的(1));

3.

如果e 不属于以上所述的情况,则假设e 的类型是T:当e 是一个左值时,decltype(e) 就是

T&;否则(e 是一个右值),decltype(e) 是T(上例中的(4) 即属于这种情况。在这个例子中,e 实际是(a->x),由于有这个括号,因此它不属于前面两种情况,所以应当以本条作为判别依据。而(a->x) 是一个左值,因此会返回double &)。说了这么多,decltype 到底有什么用?事实上,decltype 在复杂的模板编程中非常有用。不过,这需要结合我们前面提到的auto 关键字。举个经典的例子,请看下面的代码:

template

??? foo(T t, U u)

{

   return t + u;

}

问号这里该填写什么呢?问题的关键在于,我们正在处理模板,因此我们根本不知道

T 和U 的实际类型。即使这两个模板值实际都是C++ 内置类型,我们也无法确切地知道它们的和的类型。在过去的GNU C++ 运行时库中,我们可以使用前面说过的__typeof__ 扩展,编写相当难看的代码:

template

decltype((*(T*)0)+(*(U*)0)) foo(T t, U u)

{    

   return t + u;

}

而在C++11 中,我们可以使用auto 关键字:

template

auto foo(T t, U u)    -> decltype(t + u)

{

   return t + u;

}

看起来好多了吧!现在这已经是语言级别的支持了。最后,我们来看一个更加实际的例子。在GCC 的

C++11 运行时库中有这么一段代码:

template<typename _IteratorL, typename _IteratorR>

inline  auto operator-(const reverse_iterator<_IteratorL>& __x,const reverse_iterator<_IteratorR>& __y)-> decltype(__y.base()- __x.base())

{

   return __y.base()- __x.base();

}

现在,这段代码应该更加清晰了。这实际上解决了

C++ 98 实现的一个真正的bug。在GCC 的C++ 98 版本中,这段代码是这样的:

template<typename _IteratorL, typename _IteratorR>

inline

typename reverse_iterator<_IteratorL>::difference_type operator-(constreverse_iterator<_IteratorL>& __x,const reverse_iterator<_IteratorR>& __y)

{

   return __y.base()- __x.base();

}

这段代码只有在这两方的

difference_type 相同时才适用。



你可能感兴趣的:(c,decltype)