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