鉴于GMan's美味的邪恶auto_cast
实用函数炮制here,我一直试图弄清楚为什么当我试图{{1来自右值(在MSVC 10.0上)。
这是我正在使用的代码:
auto_cast
尽我所能,我试图遵循C ++ 0x引用折叠规则和概述here概述的模板参数推导规则,并且据我所知,上面给出的代码应该有效。
回想一下,在前0xC ++中,不允许引用引用:类似于A& &安培;导致编译错误。相比之下,C ++ 0x引入了以下参考折叠规则:
- A和&安培;成为A&
- A和&安培;&安培;成为A&
- A&安培;&安培; &安培;成为A&
- A&安培;&安培; &安培;&安培;成为A&&
第二个规则是函数模板的特殊模板参数推导规则,它通过对模板参数的rvalue引用获取参数:
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 }
此处适用以下规则:
- 当在类型A的左值上调用foo时,则T解析为A&amp;因此,通过上面的参考折叠规则,论证类型实际上变为A&amp ;.
- 当在类型A的右值上调用foo时,则T解析为A,因此参数类型变为A&amp;&amp;。
醇>
现在,当我将鼠标悬停在对template<typename T>
void foo(T&&);
的调用上时,工具提示会正确显示其返回值auto_cast( 5.0f )
。这意味着编译器已正确遵循规则2:
当在类型A的右值上调用foo时,则T解析为A.
因为我们有一个auto_cast_wrapper<float>
,构造函数应该实例化一个auto_cast_wrapper<float>
。但错误消息似乎暗示它实例化为按值float&&
。
这是完整的错误消息,再次显示T =正确浮动而T&amp;&amp;参数变为T?
float
有什么想法吗?
答案 0 :(得分:4)
你忘了std ::转发T&amp;&amp; auto_cast_wrapper构造函数的参数。这打破了转发链。编译器现在发出警告,但似乎工作正常。
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();
}
打印一对五。
答案 1 :(得分:2)
很抱歉发布未经测试的代码。 :)
DeadMG也是正确的,也应该转发参数。我相信警告是错误的,MSVC有一个错误。从电话中考虑:
auto_cast(T()); // where T is some type
T()
将存在于完整表达式的末尾,这意味着auto_cast
函数,auto_cast_wrapper
的构造函数和用户定义的转换都引用仍然有效对象
(由于包装器除了转换或销毁之外不能做任何事情,因此它不能超过传递给auto_cast
的值。)
我修复的可能是使成员只是T
。但是,您将进行复制/移动,而不是直接转换原始对象。但也许随着编译器的优化,它就会消失。
不,转发不是多余的。它维护了我们自动转换的价值类别:
struct foo
{
foo(int&) { /* lvalue */ }
foo(int&&) { /* rvalue */ }
};
int x = 5;
foo f = auto_cast(x); // lvalue
foo g = auto_cast(7); // rvalue
如果我没弄错,转换运算符不应该(当然不需要)标记为const
。
答案 2 :(得分:2)
它不编译的原因与编译原因的原因相同:
float rvalue() { return 5.0f }
float&& a = rvalue();
float&& b = a; // error C2440: 'initializing' : cannot convert from 'float' to 'float &&'
由于a
本身是一个左值,因此无法绑定到b
。在auto_cast_wrapper
构造函数中,我们应该再次使用std::forward<T>
来解决这个问题。请注意,我们可以在上面的具体示例中使用std::move(a)
,但这不包括也应该使用左值的通用代码。所以auto_cast_wrapper
构造函数现在变为:
template <typename T>
class auto_cast_wrapper : boost::noncopyable
{
public:
...
private:
auto_cast_wrapper(T&& pX) : mX( std::forward<T>(pX) ) { }
T&& mX;
};
不幸的是,这似乎现在表现出未定义的行为。我收到以下警告:
警告C4413:'auto_cast_wrapper :: mX':引用成员被初始化为一个在构造函数退出后不会持久的临时成员
在转换运算符被触发之前,文字似乎超出了范围。虽然这可能只是MSVC 10.0的编译器错误。从GMan's answer开始,临时的生命周期应该存在,直到完整表达式结束。