型変換可能性を含めた左辺値・右辺値識別

ある型 T に対して

  • T の左辺値
  • T の const 左辺値
  • T の右辺値
  • T に変換可能な(ただし T 自身ではない)型の値
  • T に変換可能でない型

以上を識別したい,というモチベーションに関する実験メモ.
MSVC 7.1 と GCC 3.4.4 で確認済み.要 Boost 1.33.0.

#include <iostream>
#include <boost/mpl/eval_if.hpp>
#include <boost/mpl/identity.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/type_traits/is_convertible.hpp>
#include <boost/type_traits/add_reference.hpp>
#include <boost/type_traits/remove_cv.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <boost/utility/enable_if.hpp>

template<class T>
class move_from
{
public:
  move_from(T *src)
    : p_(src)
  {}

private:
  T *p_;
};

template<class T>
struct is_movable
  : public boost::is_convertible<T, move_from<T> >
{};

template<class T>
struct move_return_type
  : public boost::mpl::eval_if<
      is_movable<T>
    , boost::mpl::identity<T>
    , boost::add_reference<T>
    >
{};

template<class T>
move_from<T> move_dispatch(T &x, boost::mpl::true_ is_movable)
{
  return move_from<T>(&x);
}

template<class T>
T &move_dispatch(T &x, boost::mpl::false_ is_movable)
{
  return x;
}

template<class T>
typename move_return_type<T>::type move(T &x)
{
  return move_dispatch(x, typename is_movable<T>::type());
}

template<class T>
struct plain
  : public boost::remove_cv<
      typename boost::remove_reference<T>::type
    >
{};

template<class T, class U, class R = void>
struct enable_if_same
  : public boost::enable_if<boost::is_same<T, U>, R>
{};

template<class T, class U, class R = void>
struct disable_if_same
  : public boost::disable_if<boost::is_same<T, U>, R>
{};

template<class T, class U, class R = void>
struct enable_if_convertible
  : public boost::enable_if<boost::is_convertible<T, U>, R>
{};

template<class T, class U, class R = void>
struct disable_if_convertible
  : public boost::disable_if<boost::is_convertible<T, U>, R>
{};

class another_type
{
public:
  another_type()
  {}

  another_type(another_type const &)
  {
    std::cout << "another_type のコピーコンストラクタ" << std::endl;
  }
};

class irrelevant_type
{
public:
  irrelevant_type()
  {}

  irrelevant_type(irrelevant_type const &)
  {}
};

class movable_type
{
public:
  movable_type()
  {}

  movable_type(movable_type const &)
  {
    std::cout << "movable_type のコピーコンストラクタ" << std::endl;
  }

  movable_type(another_type const &)
  {
    std::cout << "another_type から movable_type への変換コンストラクタ" << std::endl;
  }

  movable_type(move_from<movable_type> src)
  {
    std::cout << "movable_type の move コンストラクタ" << std::endl;
  }

  operator move_from<movable_type>()
  {
    return move_from<movable_type>(this);
  }
};

movable_type rvalue()
{
  movable_type m;
  return move(m);
}

movable_type const const_rvalue()
{
  movable_type m;
  return move(m);
}

another_type another_rvalue()
{
  another_type a;
  return a;
}

another_type const another_const_rvalue()
{
  another_type a;
  return a;
}

irrelevant_type irrelevant_rvalue()
{
  irrelevant_type i;
  return i;
}

irrelevant_type const irrelevant_const_rvalue()
{
  irrelevant_type i;
  return i;
}

template<class T>
void discriminate(
  T &x
, typename enable_if_same<T, movable_type>::type * = 0
)
{
  std::cout << "movable_type の非 const 左辺値" << std::endl;
}

template<class T>
void discriminate(
  T &x
, typename enable_if_same<T, movable_type const>::type * = 0
)
{
  std::cout << "movable_type の const 左辺値" << std::endl;
}

void discriminate(move_from<movable_type> x)
{
  std::cout << "movable_type の非 const 右辺値,もしくは明示的な move" << std::endl;
}

template<class T>
void discriminate(
  T const &x
, typename disable_if_same<typename plain<T>::type, movable_type>::type * = 0
, typename enable_if_convertible<T, movable_type>::type * =0
)
{
  std::cout << "movable_type へ変換可能な型.ただし,movable_type 自身ではない" << std::endl;
}

template<class T>
void discriminate(
  T const &
, typename disable_if_convertible<typename plain<T>::type, movable_type>::type * = 0
)
{
  std::cout << "movable_type へ変換不可能な型" << std::endl;
}

int main()
{
  using namespace std;

  movable_type lvalue;
  discriminate(lvalue);
  cout << endl;

  movable_type const const_lvalue;
  discriminate(const_lvalue);
  cout << endl;

  movable_type lvalue2;
  discriminate(move(lvalue2));
  cout << endl;

  movable_type const const_lvalue2;
  discriminate(move(const_lvalue2));
  cout << endl;

  discriminate(rvalue());
  cout << endl;

  discriminate(const_rvalue());
  cout << endl;

  another_type another_lvalue;
  discriminate(another_lvalue);
  cout << endl;

  another_type const another_const_lvalue;
  discriminate(another_const_lvalue);
  cout << endl;

  discriminate(another_rvalue());
  cout << endl;

  discriminate(another_const_rvalue());
  cout << endl;

  irrelevant_type irrelevant_lvalue;
  discriminate(irrelevant_lvalue);
  cout << endl;

  irrelevant_type const irrelevant_const_lvalue;
  discriminate(irrelevant_const_lvalue);
  cout << endl;

  discriminate(irrelevant_rvalue());
  cout << endl;

  discriminate(irrelevant_const_rvalue());
  cout << endl;
}