就像我上期所報道的一樣,在2002十月標准會議上,兩個庫擴展作為標准庫延深,而被通過。
1 是Doug Gregor’s提出的多態函數的object wrappers。
2 Jaakko Järvi's提出的tuple類型。
這兩個都是直接來在Boost項目。(Boost項目是一個C++ libraries 集合)上次,我答應在這期和下一期將介紹這兩個擴展的庫,這個月,就讓我來簡單的介紹一下tuple類型。
Tuple Types:一個簡單Motivating例子
假如你想用一個函數返回多於一個返回值,例如:
// yields a quotient only
//
int IntegerDivide( int n, int d ) {
return n / d;
}
// Sample use:
cout << "quotient = " << IntegerDivide( 5, 4 );
在這個實現中有什麼錯誤嗎? 也許沒有,畢竟在編譯器中,我們內嵌了整數除法。包括結果也能夠四捨五入。
但是,如果我們想做更多。特別,想提供一個方法得到除法其他的信息,例如除法的余數。如果沒有改變函數的結構。那麼,實現這樣的要求的函數不是一件容易的事情。
一種辦法我們在函數中加入一個輸出變量。
// Example 1(b): Integer division,
// yielding a quotient and remainder,
// one as the return value and one via
// an output parameter
//
int IntegerDivide( int n, int d, int& r ) {
r = n % d;
return n / d;
}
// Sample use:
int remainder;
int quotient = IntegerDivide( 5, 4, remainder );
cout << "quotient = " << quotient
<< "remainder = " << remainder;
這個方法的實現比較,但是我們經常這麼實現。這種通過返回值和輸出變量來返回函數返回值的辦法,看起來有點不可思議。有人也許會說下面的辦法更好。
// Example 1(c): Integer division,
// yielding a quotient and remainder,
// this time via two output parameters
//
void IntegerDivide( int n, int d, int& q, int& r ) {
r = n % d;
q = n / d;
}
// Sample use:
int quotient, remainder;
IntegerDivide( 5, 4, quotient, remainder );
cout << "quotient = " << quotient
<< "remainder = " << remainder;
這種辦法也許更加協調。但是 還是比較含糊,不令人滿意。稍微想一想,我們會記得為什麼:Ralph Waldo Emerson建議我們:“一個愚笨的一致性的想法是思想混亂的怪物”(a foolish consistency is the hobgoblin of little minds)。這個版本能夠正常工作,但是,如果你認為它不穩定的話,我不會責怪你。
那麼該怎麼做呢?在這一點我們通常會想起在標准庫中我們有一個工具:std::pair,畢竟在標准模板庫中有很多函數可以返回幾個值 ,iterator范圍就是作為一個單獨的值-同時,大多通過pair<iterator,iterator>實現的,同樣的方法能夠運行,如下:
// Example 1(d): Integer division,
// yielding a quotient and remainder,
// this time both in the return value
//
std::pair<int,int> IntegerDivide( int n, int d ) {
return pair<int,int>( n/d, n%d );
}
// Sample use:
pair<int, int> quot_rem = IntegerDivide( 5, 4 );
cout << "quotient = " << quot_rem.first
<< "remainder = " << quot_rem.second;
可以看出這是一個滿意的做法,同時,它還可以提高。
Tuples in Action
一些語言,包括Haskell, ML, 以及Python,都直接支持tuple types。C++不是這樣,這是因為C++是一個能做任何事情,和內建標准庫的系統語言,因此,我們能夠,以庫的形式實現我們自己的tuple types。像java等語言是把tuple type作為pair一個系列打包在一起。一個 tuple
type和“bundle-o-values”很相像。
在下面一個tuple-ized 的IntegerDivide例子和上面pair-ized 是很相像的,但是,我們不要被迷惑了,畢竟它使用的是一種新的方法:
// Example 2(a): Integer division,
// yielding a quotient and remainder,
// via a type return type
//
tuple<int,int> IntegerDivide( int n, int d ) {
return tuple<int,int>( n/d, n%d );
}
// Sample use:
tuple<int, int> quot_rem = IntegerDivide( 5, 4 );
cout << "quotient = " << quot_rem.get<0>()
<< "remainder = " << quot_rem.get<1>();
這個例子的語法沒有pair那麼優雅,但是,它卻是和pairs一樣的簡單好用。
另一方面,typle 不局限於只有兩個成員,它可以有任意多的成員,因此,它可以捆綁任何多個數值,我們來看下面的例子:
// Example 2(b): Floating-point division,
// yielding a quotient and remainder,
// but also an underflow
//
tuple<float, float, bool> // quotient, remainder, underflow
FloatDivide( float n, float d ) {
// —
}
如果,我們使用std::pair來實現的話,那麼將會是這樣,std::pair<float, std::pair<float, bool> >, (譯注:這樣大家也許能夠看出tuple的優勢了把)
但是,我們不能老是把tuple作為bundle-o-values來使用。這裡有一些方法把來說怎樣把一些獨立的變量捆綁成tuple 。這是對於捆綁數值和解綁數值都是有用。例如,我們回到第一個關於除法例子 。
// Example 3: Bundling and unbundling
// using "tie"
//
tuple<int,int> IntegerDivide( int n, int d ) {
return tuple<int,int>( n/d, n%d );
}
// Sample use:
int quotient, remainder;
tie( quotient, remainder ) = IntegerDivide( 5, 4 );
cout << "quotient = " << quotient
<< "remainder = " << remainder;
通過這種方法,我們就不用寫那些我們不喜歡寫的輸出變量了,Tuples有自己的輸入,輸出符號,和解壓操作符號。
// Example 4(a): Streaming tuples
//
tuple<int, int> quot_rem = IntegerDivide( 5, 4 );
cout << quot_rem; // "(1 1)"
另一方面,如果,你想發揮一下你才智的話,你可以控制括號,和分界符。
// Example 4(b): Customizing streamed tuples
//
tuple<int, int> quot_rem = IntegerDivide( 5, 4 );
cout << tuples::set_open('['] << tuples::set_close(')')
<< tuples::set_delimiter(',')
<< quot_rem; // "[1,1]"
你如果有興趣,你可以參考Boost中tuple的實現,(www.boost.org),