std::tuple のメタ関数と get をちょっとだけ改良する

C++0x から新たに標準ライブラリに加わった「任意個の値の組」を扱うクラス std::tuple には、
std::tuple_size と std::tuple_element という二種類のメタ関数が用意されています:

#include <tuple>
#include <string>
#include <type_traits> // std::is_same
int main()
{
  typedef std::tuple<int, double, std::string> tuple_type;
  static_assert( std::tuple_size<tuple_type>::value == 3, "error." );
  static_assert( std::is_same<std::tuple_element<0, tuple_type>::type, int>::value, "error." );
  static_assert( std::is_same<std::tuple_element<1, tuple_type>::type, double>::value, "error." );
  static_assert( std::is_same<std::tuple_element<2, tuple_type>::type, std::string>::value, "error." );
}


こいつはタプルの要素数や要素型によって処理を切り替えたい時などに重宝しますが、
tuple_size や tuple_element に渡す tuple が const や参照で修飾されていると、呼び出すことが出来ません。

std::tuple_size<std::tuple<int, double> const>::value // ill-formed
std::tuple_size<std::tuple<int, double> &&>::value    // ill-formed
std::tuple_element<0, std::tuple<int, double>&>::type // ill-formed


そこで、 const や参照で修飾された tuple に対しても使うことのできる
tuple_size や tuple_element や、ついでに使用例として rvalue reference に対応した get も作ってみました。

#include <tuple>
#include <type_traits>

namespace gintenlib
{
 namespace etude // C++0x の練習、ということで etude っていう名前空間に入れてみる
 {
  // タプルの大きさ。参照や const が外されてる場合に対応。
  template<typename Tuple>
  struct tuple_size
    : std::tuple_size<
        typename std::remove_const<
          typename std::remove_reference<Tuple>::type
        >::type
      > {};
  
  // タプルの要素の型。 const や参照に対して、それっぽい型を返す。
  // まず値に対しては普通に std::tuple_element を使う
  template<std::size_t I, typename Tuple>
  struct tuple_element
    : std::tuple_element<I, Tuple> {};
  
  // 参照に対して
  template<std::size_t I, typename Tuple>
  struct tuple_element<I, Tuple&>
    : std::add_lvalue_reference<
        typename tuple_element<I, Tuple>::type
      > {};
  template<std::size_t I, typename Tuple>
  struct tuple_element<I, Tuple&&>
    : std::add_rvalue_reference<
        typename tuple_element<I, Tuple>::type
      > {};
  // const 修飾に対して
  template<std::size_t I, typename Tuple>
  struct tuple_element<I, Tuple const>
    : std::add_const<
        typename tuple_element<I, Tuple>::type
      > {};
  
  // tuple の get 
  // std::get と違い、 rvalue reference に対して最適化されている
  template<std::size_t I, typename Tuple>
  inline typename tuple_element<I, Tuple&&>::type tuple_get( Tuple && x )
  {
    return static_cast<typename tuple_element<I, Tuple&&>::type>( std::get<I>(x) );
  }
  
 }  // namespace etude
}   // namespace gintenlib

こんな感じです。
例は面倒なので出しませんが、 tuple_get は rvalues の時にはキチンと move してくれます。
無論、左辺値参照が渡された場合には、ちゃんと std::get と同じように動いてくれます。


まぁでも正直 tuple から move したい局面って限られてるのかも。
prvalue からだと1つしか値を得られないしですし、
move した場合だと、何回かに分ける必要があるので、なんとなく怖いですよね。