[英]C++0x rvalue reference template argument deduction
鑒於GMan 在這里炮制的美味邪惡的auto_cast
實用程序函數,我一直在試圖弄清楚為什么當我嘗試auto_cast
值(在MSVC 10.0上) auto_cast
時它不能為我編譯。
這是我正在使用的代碼:
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
}
盡我所能,我已經嘗試遵循C ++ 0x引用折疊規則和此處概述的模板參數推導規則,並且據我所知,上面給出的代碼應該可行。
回想一下,在0x-C ++之前,不允許引用引用:A &&之類的東西會導致編譯錯誤。 相比之下,C ++ 0x引入了以下參考折疊規則:
- A &&成為A&
- A &&&成為A&
- A &&&成為A&
- A && &&成為A &&
第二個規則是函數模板的特殊模板參數推導規則,它通過對模板參數的rvalue引用獲取參數:
template<typename T> void foo(T&&);
此處適用以下規則:
- 當在類型A的左值上調用foo時,則T解析為A&,因此,通過上面的參考折疊規則,參數類型實際上變為A&。
- 當在類型A的右值上調用foo時,則T解析為A,因此參數類型變為A &&。
現在,當我將鼠標懸停在對auto_cast( 5.0f )
的調用上時,工具提示正確顯示其返回值為auto_cast_wrapper<float>
。 這意味着編譯器已正確遵循規則2:
當在類型A的右值上調用foo時,則T解析為A.
因為我們有一個auto_cast_wrapper<float>
,構造函數應該實例化以獲取一個float&&
。 但是錯誤消息似乎意味着它實例化為按值float
。
這是完整的錯誤消息,再次顯示T =正確浮動而T &&參數變為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 ]
有什么想法嗎?
你忘了std ::將T &&參數轉發給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();
}
打印一對五。
很抱歉發布未經測試的代碼。 :)
DeadMG也是正確的,也應該轉發參數。 我相信警告是錯誤的,MSVC有一個錯誤。 從電話中考慮:
auto_cast(T()); // where T is some type
T()
將存活到完整表達式的末尾,這意味着auto_cast
函數, auto_cast_wrapper
的構造函數和用戶定義的轉換都引用了一個仍然有效的對象。
(由於包裝器除了轉換或銷毀之外不能做任何事情,因此它不能比傳遞給auto_cast
的值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
。
它不編譯的原因與為什么不編譯原因相同:
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)
,但這不會涵蓋應該與lvalues一起使用的通用代碼。 所以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的回答來看,一個臨時的生命應該存活到完整表達的結束。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.