effective C++ 条款 47:使用traits classes表现类型信息
stl主要由“用以表現(xiàn)容器、迭代器和算法”的template構(gòu)成,但也覆蓋若干工具性的templates,其中一個(gè)名為advance,將某個(gè)迭代器移動(dòng)某個(gè)給定距離:
template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d);??? //將迭代器向前移動(dòng)d個(gè)單位,d<0則向后移動(dòng)。
stl迭代器的分類:
Input迭代器只能向前移動(dòng),一次一步,只讀,且只能讀取一次。模仿指向輸入文件的閱讀指針:istream_iterator是代表
Output迭代器類似,但只可涂寫它們所指的東西,ostream_iterator是代表。
它們只適合“一次性操作算法”one-pass algorithm。
forward迭代器,可以做前述兩種分類所能做的每一件事,而且可以讀或?qū)懫渌肝镆淮我陨稀?墒┬杏诙啻涡圆僮魉惴ātl未提供單向linked_list,指入這種容器的迭代器就是屬于forward迭代器。
bedirectional迭代器比上一個(gè)威力更大:除了可以向前移動(dòng),還可以向后移動(dòng)。stl的list、set、multiset、map和multimap的迭代器都屬于這一類。
random access迭代器比上一個(gè)更威力大,它可以執(zhí)行迭代器算術(shù),常量時(shí)間內(nèi)可以向前或向后跳躍任意距離。內(nèi)置指針也可以當(dāng)random迭代器用。vector、deque、string提供的迭代器都是這一類。
這5種分類,c++標(biāo)準(zhǔn)程序庫(kù)分別提供專屬的卷標(biāo)結(jié)構(gòu)(tag struct)加以確認(rèn):
struct input_iterator_tag{};
struct output_iterator_tag{};
struct forward_iterator_tag: public input_iterator_tag{};
struct bidirectional_iterator_tag: public forward_iterator_tag{};
struct random_iterator_tag: public bidirectional_iterator_tag{};
我們真正希望的是以這種方式實(shí)現(xiàn)advance:
template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d)
{
??? if (iter is a random_access_iterator)
??? {
??????? iter += d;??????????? //針對(duì)random_access迭代器使用迭代器算術(shù)運(yùn)算
??? }
??? else
??? {
??????? if (d >= 0)
??????? {
??????????? while (d--)
??????????? {
??????????????? ++iter;
??????????? }
??????? }
??????? else
??????? {
??????????? while (d++)
??????????? {
??????????????? --iter;
??????????? }
??????? }
??? }
}
這種做法必須先判斷iter是否為random_access迭代器,需要取得類型的某些信息。那就是traits讓你進(jìn)行的事:它們?cè)试S在編譯期間取得某些類型信息。
traits并不是c++關(guān)鍵字或一個(gè)預(yù)先定義好的構(gòu)件:它們是一種技術(shù),也是一個(gè)c++程序員共同遵守的協(xié)議。這個(gè)技術(shù)的要求之一是,它對(duì)內(nèi)置類型和用戶自定義類型的表現(xiàn)必須一樣好。
“traits必須能夠施行于內(nèi)置類型”意味著“類型內(nèi)的嵌套信息”這種東西出局了,因?yàn)槲覀儫o(wú)法將信息嵌套于原始指針內(nèi)。因此類型的traits信息必須位于類型自身之外。標(biāo)準(zhǔn)技術(shù)是把它放進(jìn)一個(gè)template及其一或多個(gè)特化版本中。這樣的templates在標(biāo)準(zhǔn)庫(kù)中有若干個(gè),其中針對(duì)迭代器的被命名為iterator_traits:
template<typename IterT>//template,用來(lái)處理迭代器分類的相關(guān)信息
struct iterator_traits;
習(xí)慣上traits總是被實(shí)現(xiàn)為structs,但它們卻又往往被稱為traits classes。
針對(duì)每個(gè)IterT,在struct iterator_traits<IterT>內(nèi)一定聲明某個(gè)typedef名為iterator_category。這個(gè)typedef用來(lái)確認(rèn)IterT的迭代器分類。
兩個(gè)部分實(shí)現(xiàn)上述所言。首先它要求每一個(gè)“用戶自定義的迭代器類型”必須嵌套一個(gè)typedef,名為iterator_category,用來(lái)確認(rèn)適當(dāng)?shù)木順?biāo)結(jié)構(gòu)(tag struct)。例如:deque和list的迭代器的樣子
template<...>
class deque{
public:
??? class iterator{
??? public:
??????? typedef random_access_iterator_tag iterator_category;
??? };
};
template<...>
class list{
public:
??? class iterator{
??? public:
??????? typedef bidirectional_iterator_tag iterator_category;
??? };
};
至于iterator_traits,只是鸚鵡學(xué)舌般地響應(yīng)iterator class的嵌套式typedef:
template<typename IterT>
struct iterator_traits{
??? typedef typename IterT::iterator_category iterator_category;
??? ...
};
iterator_traits的第二部分,專門用來(lái)對(duì)付指針,因?yàn)橹羔槻缓瑃ypedef。為了支持指針迭代器,iterator_traits特別針對(duì)指針類型提供了一個(gè)偏特化版本(partial template specialization)。指針的行徑與random_access迭代器相似,多以iterator_traits為指針指定的迭代器類型是:
template<typename IterT>??????? //template 偏特化,針對(duì)內(nèi)置指針
struct iterator_traits<IterT*>{
??? typedef random_access_iterator_tag iterator_category;
};
實(shí)現(xiàn)一個(gè)traits class:
確認(rèn)托干你希望將來(lái)可取得的類型相關(guān)信息。例如迭代器而言,我們希望將來(lái)可以取得其分類(category)。
為該信息選擇一個(gè)名稱(例如iterator_category)
提供一個(gè)template和一組特化版本,內(nèi)含你希望支持的類型相關(guān)信息。
template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d)
{
??? if (typeid(typename std::iterator_traits<IterT>::iterator_category)
??????? == typeid(std::random_access_iterator_tag))
??? {
??????? ...
??? }
}
并非我們想要的,首先會(huì)導(dǎo)致編譯問題(條款48)。IterT類型在編譯期間獲得,所以iterator_traits<IterT>::iterator_category也可以在編譯期間確定。但if語(yǔ)句卻是在運(yùn)行期才會(huì)核定。這不僅浪費(fèi)時(shí)間,也造成可執(zhí)行文件膨脹。
我們真正想要的是一個(gè)條件式判斷“編譯期核定成功”之類型。c++有一個(gè)取得這種行為的辦法,那就是重載(overloading)
你必須詳細(xì)敘述各個(gè)重載件的參數(shù)類型。當(dāng)你調(diào)用f,編譯器便根據(jù)傳來(lái)的實(shí)參選擇最適當(dāng)?shù)闹剌d件。
template<typename IterT, typename DistT>??????????????????????????????????????????? //這份實(shí)現(xiàn)于
void doAdvance(IterT& iter, DistT d, std::random_access_iterator_tag)??????? //random access
{??????????????????????????????????????????????????????????????????????????????????????????????????????????????? //迭代器
??? iter += d;
}
template<typename IterT, typename DistT>??????????????????????????????????????????? //這份實(shí)現(xiàn)于
void doAdvance(IterT& iter, DistT d, std::bidirectional_iterator_tag)??????????? //biderectional
{??????????????????????????????????????????????????????????????????????????????????????????????????????????????? //迭代器
??? if (d >= 0) {while (d--) ++iter;}
??? else {while(d++) --iter;}
}
template<typename IterT, typename DistT>??????????????????????????????????????????? //這份實(shí)現(xiàn)于
void doAdvance(IterT& iter, DistT d, std::input_iterator_tag)??????????????????????? //input
{??????????????????????????????????????????????????????????????????????????????????????????????????????????????? //迭代器
??? if (d < 0){throw std::out_of_range("Negative distance");}
??? while (d--) ++iter;
}
由于forward_iterator_tag繼承自input_iterator_tag,所以上述doAdvance的input_irerator_tag版本也能夠處理forward迭代器。
advance需要做的只是調(diào)用它們并額外傳遞一個(gè)對(duì)象,后者必須帶有適當(dāng)?shù)牡鞣诸悺?/p>
template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d)
{
??? doAdvance(iter, d, typename std::iterator_traits<IterT>::iterator_category());
}
使用traits class:
建立一組重載函數(shù)(身份像勞工)或函數(shù)模板,(例如doAdvance),彼此間的差異只在于各自的traits參數(shù)。令每個(gè)函數(shù)實(shí)現(xiàn)碼與其接受的traits信息相應(yīng)和。
建立一個(gè)控制函數(shù)(身份像工頭)或函數(shù)模板,例如advance,它調(diào)用那些“勞工函數(shù)”并傳遞traits class所提供的信息。
轉(zhuǎn)載于:https://www.cnblogs.com/lidan/archive/2012/02/17/2356106.html
總結(jié)
以上是生活随笔為你收集整理的effective C++ 条款 47:使用traits classes表现类型信息的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java继承时构造函数的关系
- 下一篇: PHP上传注意事项