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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With