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 相同時才適用。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章