在調用算法函數的時候,有時候會把不同序列的迭代器當成同一序列使用。
下面提供避免這樣錯誤的一種機制。
本內容來自TCPL(特別版)
有時候,我們會犯這樣的錯誤,把兩個不同序列的迭代器去構成一個序列了。比如:
void
?f(list
<
string
>&
?fruit,?list
<
string
>&
?citrus)
{
?typedef?list
<
string
>
::const_iterator?LI;
?LI?p1?
=
?find(fruit.begin(),?citrus.end(),?
"
apple
"
);?
//
?error,?不在同一序列
?LI?p2?
=
?find(fruit.begin(),?fruit.end(),?
"
apple
"
);
?LI?p3?
=
?find(citrus.begin(),?citrus.end(),?
"
pear
"
);
?LI?p4?
=
?find(p2,?p3,?
"
peach
"
);?
//
?這個更加隱蔽。
}
這里Bjarne Stroustrup給出一個解決問題的途徑。其關鍵就是用整個容器代替x.begin, x.end()的輸入。
這樣,我們要封裝兩個東西,1、find()函數。 2、begin(),end()
利用重載,封裝find函數。
template
<
class
?In,?
class
?T
>
?
In?find(Iseq
<
In
>
?r,?
const
?T
&
?v)??
//
通過重載機制,得到這個find的擴充版本。
{
????
return
?find(r.first,?r.second,?v);?
//
標準庫中的find
};
?
利用對偶,封裝迭代器。
首先,我們構造一個Iseq以保證迭代器是統一序列成對輸入的。
template
<
class
?In
>
struct
?Iseq:?
public
?pair
<
In,?In
>
{
????Iseq(In?i1,?In?i2):?pair
<
In,?In
>
(i1,?i2){}
};
?
接著構造一個協助函數,直接傳遞容器。
template
<
class
?C
>
Iseq
<
typename?C::iterator
>
?iiseq(C
&
?c)?
//
C為容器
{
????
return
?Iseq
<
typename?C::iterator
>
(c.begin(),?c.end());
}
這樣,我們可以利用上面的機制,來避免所提出的錯誤。
void
?f(list
<
string
>&
?fruit,?list
<
string
>&
?citrus)
{
?typedef?list
<
string
>
::const_iterator?LI;
?LI?p1?
=
?find(iiseq(fruit),?
"
apple
"
);?
?LI?p2?
=
?find(iiseq(citrus),?
"
apple
"
);
?LI?p3?
=
?find(citrus.begin(),?citrus.end(),?
"
pear
"
);?
//
}
?
下面我們仔細分析整個機制的幾個細節。
先讓我們來看看pair的樣子。
template
<
class
?T1,?
class
?T2
>
struct
?std::pair{?
//
這里用struct來定義
????typedef?T1?first_type;
????typedef?T2?second_type;
????T1?first;
????T2?second;
????pair():?first(T1()),?second(T2()){}
????pair(
const
?T1
&
?x,?
const
?T2
&
?y):?first(x),?second(y){}
????template
<
class
?U,?
class
?v
>
????????pair(
const
?pair
<
U,?V
>&
?p):?first(p.first),?second(p.second){}
????
//
};
注意pair的兩個數據成員first, second都是public的,所以Iseq繼承pair之后可以直接訪問。
考察find()函數的重載版本
find(Iseq<In> r, const T& v)
注意“Iseq<In> r”使用值傳遞,而不用引用傳遞(Iseq<In>& r)。
這是因為iiseq協助函數返回一個臨時對象,所以在find中,不能用引用傳遞。
template<class C>Iseq<typename C::iterator> iiseq(C& c) //C為容器
{
?return Iseq<typename C::iterator>(c.begin(), c.end());
}
大家可能會考慮到效率問題,覺得值傳遞可能不妥。其實不然,我們可以發現,Iseq里面的數據成員是兩個Iterator,一般來說不是很大(有時,就是兩個指針),在效率上不會產生很大的影響。
還有這里代碼中出現typename,(如return Iseq<typename C::iterator>(c.begin(), c.end());) 可能對初學者來說有些生疏。為什么不直接寫: Iseq<C::iterator>(c.begin(), c.end())。這是由于編譯器不能直接認出C::iterator是一種類型,所以我們加上修飾符號typename告訴編譯器C::iterator使用一種類型。
?