簡體   English   中英

C ++ 0x右值參考模板參數推導

[英]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&&); 

此處適用以下規則:

  1. 當在類型A的左值上調用foo時,則T解析為A&,因此,通過上面的參考折疊規則,參數類型實際上變為A&。
  2. 當在類型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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM