Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++0x rvalue reference template argument deduction

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:

  1. 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&.
  2. 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.

error showing tooltip

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?

like image 434
dvide Avatar asked Oct 27 '10 13:10

dvide


1 Answers

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.

like image 144
Puppy Avatar answered Sep 23 '22 19:09

Puppy