汎用射影関数と一時オブジェクトの寿命
複数のオブジェクトを単に1つにまとめ上げることだけを目的としたクラス,例えば std::pair や boost::tuple をタプル様クラスと呼ぶことにする.このとき,これらタプル様クラスに対してそのオブジェクトが保持する複数のオブジェクトの一部を取得するという操作が考えられる.今,このような操作を射影と呼ぶことにする.
この射影という操作を全ての std::pair あるいは boost::tuple などに対して汎用化することを考える.例としては, ISO/IEC 14882:2003 には規定されていないが, SGI STL などでは select1st 及び select2nd という関数オブジェクトが規定されている.
ここでタプル様クラスに保持されているオブジェクトのコピーが重い,という状況を想定する.あるいは,保持されているオブジェクトの参照を取得したい,いう状況を想定する.このような状況に対応するためには当然,汎用射影関数(オブジェクト)が参照を返すようにすれば良いが,これは見た目ほど単純ではない.
template< class Pair > typename Pair::first_type &select1st( Pair &p ) { return p.first; } template< class Pair > typename Pair::first_type const &select1st( Pair const &p ) { return p.first; }
上記のコード(のうち特に後者)は明らかに誤りとなる.後者の宣言は一時変数(右辺値)を束縛することができ,その一時変数
結局,上記の問題は const 左辺値と右辺値を識別するという問題,すなわち右辺値参照 (Move Semantics) の問題とほぼ同一視されることになる.
現在の段階で上記の問題を解決するには,値のコピーを返す射影演算と参照を返す射影演算を明示的に分けるしかない.
template< class Pair > typename Pair::first_type select1st( Pair const &p ) { return p.first; } template< class Pair > typename boost::disable_if< boost::is_const< Pair > , typename Pair::first_type &select1st >::type project1st( Pair &p ) { return p.first; } template< class Pair > typename boost::enable_if< boost::is_const< Pair > , typename Pair::first_type const &select1st >::type project1st( Pair &p ) { return p.first; }
project1st は非 const 参照を引数に受けるので,一時オブジェクトを束縛できず(const な右辺値を束縛できるが,)