関数テンプレートにおける右辺値識別

完全に個人的なメモなので気にしないよ〜に.

#include <iostream>

#include <boost/mpl/identity.hpp>
#include <boost/mpl/if.hpp>
#include <boost/type_traits/is_volatile.hpp>
#include <boost/static_assert.hpp>

struct volatile_refused;

template< class T, class R = void >
struct refuse_volatile
  : public boost::mpl::if_<
        boost::is_volatile< T >
      , volatile_refused
      , boost::mpl::identity< R >
      >::type
{};

template< class T >
typename refuse_volatile< T, void >::type
discriminate( T & )
{
  std::cout << "L" << std::endl;
}

template< class T >
void discriminate( T const volatile & )
{
  // If you want to get nonvolatile `T', use `const_cast< T const & >' here.
  // Such `const_cast' is completely legal.
  std::cout << "R" << std::endl;
}



int return_rvalue()
{
  return 0;
}

int const return_const_rvalue()
{
  return 0;
}

int main()
{
  int i = 0;
  discriminate( i );                     // -> "L".
  int const ci = 0;
  discriminate( ci );                    // -> "L".
  discriminate( 0 );                     // -> "R".
  discriminate( return_rvalue() );       // -> "R".
  discriminate( return_const_rvalue() ); // -> False "L".
                                         // This is the only remaining
                                         // problem. In the current standard,
                                         // this may be unsolvable (If it
                                         // were resolved, it would be an
                                         // rvalue reference emulation).
  int volatile vi = 0;
  //discriminate( vi );       // Triggers an expected compile error.
  int const volatile cvi = 0;
  //discriminate( cvi );      // Triggers an expceted compile error.
}

#あ,違うわ.右辺値識別は(ほぼ)解けているけれど the Perfect Forwarding にはなってないや.

コピーを伴わずに左辺値->右辺値変換を行う

template< class T >
T f( T &x )
{
  return true ? x : f( x );
}

上記のコード片中の3項演算子の計算結果は右辺値だが,依然としてオブジェクト x を指し続けている(つまりコピーが行われない).


5.16 Conditional operator
3 Otherwise, if the second and third operand have different types, and either has (possibly cv-qualified) class type, an attempt is made to convert each of those operands to the type of the other. The process for determining whether an operand expression E1 of type T1 can be converted to match an operand expression E2 of type T2 is defined as follows:

  • If E2 is an lvalue: E1 can be converted to match E2 if E1 can be implicitly converted (clause 4) to the type “reference to T2”, subject to the constraint that in the conversion the reference must bind directly (8.5.3) to E1.
  • If E2 is an rvalue, or if the conversion above cannot be done:
  • if E1 and E2 have class type, and the underlying class types are the same or one is a base class of the other: E1 can be converted to match E2 if the class of T2 is the same type as, or a base class of, the class of T1, and the cv-qualification of T2 is the same cv-qualification as, or a greater cvqualification than, the cv-qualification of T1. If the conversion is applied, E1 is changed to an rvalue of type T2 that still refers to the original source class object (or the appropriate subobject thereof). [Note: that is, no copy is made. ]

(以下略)

普通は役に立たない C++ の知識ベスト100には入るなこれ.とはいえ,役に立つと思ったから調べたんだけれど.