重複老話題–C++ template tricks and techniques

來源:互聯網
上載者:User

這篇文章涉及的內容已經不是什麼新鮮玩意了。對於部分三角和全體星級的使用者來說,這隻是些小把戲。我從眾多的小把戲中挑了幾個出來說明了一下,也沒有什麼高深的概念,至於我寫這個的目的,其實也沒什麼,閑來無事吧。

1,獲得一個指標進行一次解引用之後的類型。
指標在解引用一次之後會得到它引用對象的類型。我們可以用偏特化來得到這個類型。
template
struct rm_a_ptr
{
typedef T value_type;
};

template
struct rm_a_ptr
{
typedef T value_type;
};

rm_a_ptr ::value_type I = 5;

當然我們可以獲得移出所有ptr,只需要把這個偏特化變通一下。這裡就不用討論了。

2,static_if
static_if就是在編譯期的時候進行條件控制。其實#ifXXX這些編譯前置處理器就是做這些事情的,不過static_if還有一個額外的動作就是讓語言的類型系統也參與運算。

template
struct static_if
{
typedef TrueType value_type;
};

template
struct static_if
{
typedef FalseType value_type;
};

用模板偏特化判斷第一個非類型模板參數進而決定選用TrueType和FalseType。

3,判斷兩個類型是否相同。
C++提供了對函數的重載,編譯器在一系列的重載函數中來尋找首選。這是我們實作類別型判斷的基本依據。為此我們需要兩個函數來完成這種匹配。
template int match(T);
template char match(…);
這兩個函數都可以接納所有類型的參數,但是第一個match可以讓它只接納指定的參數類型,來看看下面的代碼。
match (1);
通過 來指定模板參數T為int,那麼第一個match的參數就是int,而1正是int,所以這裡的首選是第一個match而不是第二個。基於這一點,我們就可以完成兩個類型是否相同的判斷了。

template
struct same_type
{
enum{ value = (sizeof(int) == sizeof(match ((U**)0))) };
};

match ((U**)0) 發生了什麼?
我們把null 指標0顯式轉換成U**然後拿給match與T**匹配。如果U和T是同一個類型,則match傳回值的類型是int。 如果U和T不是同一個類型,則匹配第二個match,其傳回值類型就是char。所以把sizeof(int)的值和sizeof match進行比較就可以把匹配的結果利用在編譯期當中,然後再把這個值交由enum中的value來儲存。

為什麼不用match (U())來進行匹配呢?因為如果U派生自T的話,那麼匹配第一個match,儘管U和T的關係是is a,但是在這種情況下把他們當作成不是一樣的更好。不然下面的例子你會犯愁
class A{};
class B: public A{};

bool x = same_type ::value;
if(x == same_type ::value) //永遠都else.

那為什麼要用二維指標而不直接是T*和U*的比較呢?原因其實和上面一樣。

4,判斷一個類型是不是函數類型。
數組有一個特點就是,元素的類型不能為函數類型和void。由此可以用以上的方法來實現這個判斷
template char match_func_type(T(*)[2]);
template int match_func_type(…);

在使用match_func_type的時候仍然要指定模板參數
match_func_type (0);
這會匹配第二個match_func_type,因為我們指定的模板參數是一個函數類型。由此可以看出,第一個match_func_type的參數會是一個指向函數類型數組的指標,顯然這是不可能的,因為沒有函數類型的數組。所以會匹配到第二個match_func_type。注意,我們還需要排除掉void類型。

template
struct is_function_type
{
enum{value = (same_type ::value == 0) && (sizeof(int) == sizeof(match_func_type )(0))};
};
這裡的運算式的偽碼為
(F != void && match_func_type(…));

5,判斷一個類型是不是函數指標
有了上面的功能,我們可以很快實現出這個。判斷進行一次解引用之後的類型是不是函數類型就可以了。

template
struct is_function_pointer
{
enum{value = is_function_type ::value};
};

6,我們得到了什嗎?
上面通篇的論述和指導展示了C++模板的一些作用。T不僅僅只是一個容器這麼簡單。但是這些在實際工作中看似不會有什麼作用,或者只是讓代碼變得更炫的一些把戲,甚至成為證明自己比別人高明的手段。到最後,開始質疑這些代碼是否有必要出現。

7,!@#$%^&*()_+
LoadLibrary + GetProcAddress + FreeLibrary是一件煩人的事情,每次用都要判斷控制代碼,因此在一段時間裡,處理這部分我都會Copy&Paste以前的代碼,然後作點小小的修改。最後發現Copy&Paste也是一件煩人的事情,所以我寫了一個類把這個工作給封裝了。
class shared_wrapper
{
public:
shared_wrapper(const char* filename);
~shared_wrapper();
bool empty();
void* symbols(const char* symbol);
private:
HMODULE module_;
};

建構函式和解構函式分別負責LoadLibrary和FreeLibrary這是顯而易見的。empty()返回true表示指定的動態庫載入成功了。symbols則返回介面的地址。定義如下
void* shared_wrapper::symbols(const char* symbol)
{
if(empty())
throw my_excep("shared_wrapper.symbols, empty shared library");

if(0 == symbol)
throw my_excep("shared_wrapper.symbols, null symbol");

void* result = ::GetProcAddress(module_, symbol);
if(0 == result)
{
std::string what = "shared_wrapper.symbols, no symbol named ";
what += symbol;
throw my_excep(what.c_str());
}

return result;
}

後來感覺symbols返回一個void*特不爽。幹嘛不讓它返回一個我們直接可以用的函數指標呢?於是我把它改成了這樣
template
FuncPtr symbols(const char* filename)
{
//…
return (FuncPtr)result;
}

後來再一次編碼過程中,順手寫了下面這行代碼
Shared.symbols ("interface")(5);
後來編譯錯誤告訴我,我返回了一個函數。這時我頓悟, symbols應該接納一個函數類型而不僅僅只接納函數指標類型。因為最後symbols都應該返回的是一個函數指標,而指定函數類型已經提供了足夠的類型資訊了。所以我再次對symbols動了手腳。

現在symbols的模板參數可以是函數類型,也可以是函數指標類型。於是先寫一個輔助類模板來計算該模板參數的函數指標類型方便後面使用。

template
struct make_func_ptr
{
typedef typename rm_a_ptr ::value_type prototype;

typedef typename static_if ::value_type value_type;
};

在這裡,我們可以把static_if理解成下面的偽碼
prototype = typeof(*T);
if(prototype == function type)
return &prototype;
else
return int;

後面為什麼會return int呢?只是借用一下,只要不return 函數指標類型就行了,下面會看到return int的作用。

Template
typename make_func_ptr ::value_type
symbols(const char* filename)
{
typedef typename make_func_ptr ::value_type fptr_type;
if(is_function_pointer ::value == 0)
throw my_excep("shared_wrapper.symbols, template is not a function type or a function pointer type");

//…
return (fptr_type)result;
}

上面return int的作用就在symbols裡,如果return int則fptr_type就是int,那麼下面的if就會捕捉到這個錯誤。這就是最後的模樣了。

8,重新看待這些夠炫的小把戲。
用代碼來實現一些語言未曾提供的特性,難道這不是靈活和強大的表現嗎?總是看到一部分人說這沒用、那沒用,也許是他們沒有考慮過這些該怎麼用。應用是否得當一切都來自於對問題的把握和理解。各位的觀點是什嗎?

本文轉自
http://topic.csdn.net/u/20071013/19/c157fda3-1e2d-4f04-9550-f7d7906dd5d1.html

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.