一段C++的讀書筆記
給定任意2個類型U和T,你如何來確定U是否繼承于T呢?在編譯時發現兩個類型的這種關系對于泛型庫的優化是極為重要的。在泛型函數中,如果某個類實現了特定的接口,你可以根據這種關系為其利用特定的優化算法。另外,如果我們可以在編譯期決定2個類的關系,我們也可以遠離dynamic_cast,從而避免運行時的效率開銷。
在著手解決這個問題之前,我們先來考慮一個更為一般的問題。假設我們有2個任意類型U和T,如何確定T能否自動轉換成U呢?
答案也許讓你有些驚訝,我們可以利用sizeof來幫忙。你可以把sizeof用在任何復雜的表達式上, sizeof可以返回這個表達式值的大小,而不會在運行時評估表達式的值。這也就意味著,你可以把函數重載、模版實例化、轉換規則等等所有你可以在C++表達式中使用的設施統統塞到sizeof中來。實際上,sizeof隱藏了一個可以演繹表達式類型的設施,最終,sizeof會返回表達式結果的類型。
這樣我們就可以通過sizeof和重載函數來解決判斷類型之間的可轉換性的問題。思路很簡單:我們提供2個重載函數,一個函數的參數是我們要轉換成的類型(我們用U表示),而另一個則用來接收其他所有類型的參數。然后我們把要檢測的類型(用T表示)傳遞給重載函數。如果接受類型U為參數的函數被調用了,我們就認為T可以轉換為U,反之則不可以。如何確定哪個函數被調用了呢?我們利用sizeof出馬,我們只要讓重載函數返回不同的類型,然后檢查一下返回值就可以了。
實踐一下:
首先,定義2個不同的類型:
typedef?
char
?Small;

class
?Big?
{?
char
?dummy[
2
];?}
;

默認情況下,
sizeof(Small)
是
1
,而
Big
的大小則無關緊要,我們只要知道肯定不是
1
就好了。
其次,定義
2
個重載函數,一個接收要轉換成的類型:
Small Test(U);
另一個用來接收“其他的所有類型”,我們要保證在排除所有的轉換之后才調用這個函數,
OK,
用省略號表示的參數列表真好滿足需求
Big Test(...);
盡管把一個
C++
對象傳遞給
...
參數類型的函數,其結果未定義,但是實際上我們并沒有調用這個函數。我們甚至可以不用實現它。
最后,我們用
sizeof
判斷一下就完成任務了:
const
?
bool
?convExist?
=
?
sizeof
(Test(T()))?
==
?
sizeof
(Small);

你也許會說,就是它了!
Test
的調用會創建一個臨時對象
T
,之后可能的結果只能是
sizeof(Small)
或
sizeof(Big)
。興奮之余,我們還要看到一個問題。如果
T
的構造函數被設計成
private
,我們就前功盡棄了。當然解決的方法也很簡單,定義一個函數,讓他返回類型為
T
的對象。
T?MakeT();
const
?
bool
?convExist?
=
?
sizeof
(Test(MakeT()))?
==
?
sizeof
(Small);

最后,把剛才的東西封裝到一個類里:
template?
<
class
?T,?
class
?U
>
class
?Conversion?
{
????typedef?
char
?Small;

????
class
?Big?
{?
char
?dummy[
2
];?}
;

????
static
?Small?Test(U);
????
static
?Big?Test(
);
????
static
?T?MakeT();

public
:

????
enum
?
{?exist?
=
?
sizeof
(Test(MakeT()))?
==
?
sizeof
(Small)?}
;
}
;

另外,我們還可以設置另外一個常量
Conversion::SameType
,如果
T
和
U
表示同一個類型,那么返回
true
:
template?
<
class
?T,?
class
?U
>
class
?Conversion?
{
????..?
as
?above..

????
enum
?
{?sameType?
=
?
false
}
}
;

之后為同一個類型設計一個偏特化版本:
template?
<
class
?T
>
class
?Conversion
<
T,?T
>
?
{

????
enum
?
{?exists?
=
?
1
,?sameType?
=
?
1
?}
;
}
;
最后,回到我們的主題,通過
Conversion
的幫助,我們可以來決定兩個類型的繼承性了。
#define
?SUPERSUBCLASS(T,?U)?\
????(Conversion
<
const
?U
*
,?
const
?T
*
)::exists?
&&
?\
????
!
Conversion
<
const
?T
*
,?
const
?
void
*>
::sameType)
當
U
繼承于
T
或者
U
和
T
是同一個類型的時候,
SUPERSUBCLASS
返回
true
??偨Y一下,只有下面這
3
種情形
const U*
可以隱式轉換到
const T*
:
1.?????
T
和
U
是同一個類型
2.?????
T
是
U
的任意一個基類
3.?????
T
是
void
我們通過第
2
個測試屏蔽了最后一種情形。當然,如果你認為同一種類型也不算是繼承關系的話,可以進一步嚴格其條件:
#define
?SUPERSUBCLASS_STRICT(T,?U)?\
????(SUPERSUBCLASS(T,?U)?
&&
?\
????
!
Conversion(
const
?T,?
const
?U)::sameType)