C++0x rvalue reference template argument deduction

c++, c++11, rvalue-reference

Solution

You forgot to std::forward the T&& argument to the auto_cast_wrapper constructor. This breaks the forwarding chain. The compiler now gives a warning but it seems to work fine.

template <typename T>
class auto_cast_wrapper
{
  public:
    template <typename R>
    friend auto_cast_wrapper<R> auto_cast(R&& pX);

    template <typename U>
    operator U() const
    {
      return static_cast<U>( std::forward<T>(mX) );
    }

  private:
    //error C2440: 'initializing': cannot convert from 'float' to 'float &&'
    auto_cast_wrapper(T&& pX) : mX(std::forward<T>(pX)) { }

    auto_cast_wrapper(const auto_cast_wrapper&);
    auto_cast_wrapper& operator=(const auto_cast_wrapper&);

    T&& mX;
};

template <typename R>
auto_cast_wrapper<R> auto_cast(R&& pX)
{
  return auto_cast_wrapper<R>( std::forward<R>(pX) );
}

float func() {
    return 5.0f;
}

int main()
{

  int c = auto_cast( func() );  // from an rvalue
  int cvar = auto_cast( 5.0f );

  std::cout << c << "\n" << cvar << "\n";
  std::cin.get();
}

Prints a pair of fives.

Problem

Given GMan's deliciously evil `auto_cast` utility function concocted here, I've been trying to figure out why it doesn't compile for me when I'm trying to `auto_cast` from an rvalue (on MSVC 10.0). Here's the code that I'm using: ``` template <typename T> class auto_cast_wrapper : boost::noncopyable { public: template <typename R> friend auto_cast_wrapper<R> auto_cast(R&& pX); template <typename U> operator U() const { return static_cast<U>( std::forward<T>(mX) ); } private: //error C2440: 'initializing': cannot convert from 'float' to 'float &&' auto_cast_wrapper(T&& pX) : mX(pX) { } T&& mX; }; template <typename R> auto_cast_wrapper<R> auto_cast(R&& pX) { return auto_cast_wrapper<R>( std::forward<R>(pX) ); } int main() { int c = auto_cast( 5.0f ); // from an rvalue } ``` To the best of my ability I've tried to follow the C++0x reference collapsing rules and the template argument deduction rules outlined here, and as far as I can tell the code given above should work. Recall that in pre-0x C++, it is not allowed to take a reference to a reference: something like A& & causes a compile error. C++0x, by contrast, introduces the following reference collapsing rules: - A& & becomes A& - A& && becomes A& - A&& & becomes A& - A&& && becomes A&& The second rule is a special template argument deduction rule for function templates that take an argument by rvalue reference to a template argument: ``` template<typename T> void foo(T&&); ``` Here, the following rules apply: - When foo is called on an lvalue of type A, then T resolves to A& and hence, by the reference collapsing rules above, the argument type effectively becomes A&. - When foo is called on an rvalue of type A, then T resolves to A, and hence the argument type becomes A&&. Now when I mouse over the call to `auto_cast( 5.0f )`, the tooltip correctly displays its return value as `auto_cast_wrapper<float>`. This meaning that the compiler has correctly followed rule 2: When foo is called on an rvalue of type A, then T resolves to A. So since we have an `auto_cast_wrapper<float>`, the constructor should instantiate to take a `float&&`. But the error message seems to imply that it instantiates to take a `float` by value. Here's the full error message, showing again that T=float correctly yet the T&& parameter becomes T? ``` main.cpp(17): error C2440: 'initializing' : cannot convert from 'float' to 'float &&' You cannot bind an lvalue to an rvalue reference main.cpp(17) : while compiling class template member function 'auto_cast_wrapper<T>::auto_cast_wrapper(T &&)' with [ T=float ] main.cpp(33) : see reference to class template instantiation 'auto_cast_wrapper<T>' being compiled with [ T=float ] ``` Any thoughts?

Original source