2015-10-16 51 views
5

當我有看起來像這樣的代碼:完美轉發和模板

template<class T> 
void f_(const T& arg) 
{ 
    cout << "void f(const T& arg): Cannot modify\n"; 
} 

template<class T> 
void f_(T&& arg) 
{ 
    cout << "void f(T&& arg): Can modify\n"; 
} 

,並在主我把它叫做:

int main() 
{ 

    MemoryBlock block; 
    f_(block); 
    f_(MemoryBlock()); 
    return 0; 
} 

輸出是:
「無效F(T & & arg):可以修改\ n「;
「void f(T & & arg):可以修改\ n」;

但當我這個代碼改爲不通用,即不是函數模板,我會定期的功能,

void f(const MemoryBlock&) 
{ 
    cout << "In f(const MemoryBlock&). This version cannot modify the parameter.\n"; 
} 

void f(MemoryBlock&&) 
{ 
    cout << "In f(MemoryBlock&&). This version can modify the parameter.\n"; 
} 

輸出更加「直觀」:
「駐f (const MemoryBlock &)。該版本不能修改參數。
「In f(MemoryBlock & &)。此版本可以修改參數。

它在我看來,只有通過將功能從模板更改爲非模板,才能完全改變右值引用的演繹規則。
如果有人會向我解釋這個問題,那真是太棒了。

+1

你通過誤導以爲模板參數'最好的選擇T'映射到'MemoryBlock'。 – Walter

+0

有趣的是,你在標題中有「完美轉發」,但你根本沒有做任何轉發。 – Mehrdad

回答

4

當您使用T&&時,這不是右值引用,而是通用參考參數。他們以相同的方式宣佈,但他們的行爲有所不同。

當您刪除模板參數時,您不再處於可推論的上下文中,它實際上是右值引用:當​​然,它們僅綁定到右值。

在可推論的上下文中(即發生類型推演時),T&&可以是左值引用左值引用。通用引用可以綁定到幾乎所有組合(const,const volatile等),並在您的情況下:const T&

現在你的思路是對rvalues和左值是有效的和重載的,但是會發生什麼是在推導模板參數時通用引用超載更好地匹配。因此,它將通過const T&過載來選擇。

通常,您只想保留通用參考函數並將其與std::forward<T>()配對以完善參數(s)。這消除了您的const T&過載需求,因爲通用參考版本將接管。

請注意,僅僅因爲你看到&&在可推論的背景下,並不意味着它是一個普遍的參考;在&&需要被追加到所推導出的類型,所以這裏實際上是一個右值引用的一些例子:

template<class T> 
void f_(std::vector<T>&& arg) // this is an rvalue reference; not universal 
{ 
    cout << "void f(T&& arg): Can modify\n"; 
} 

這裏的關於此事的精彩演講:https://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Scott-Meyers-Universal-References-in-Cpp11

+2

@Artur另請參閱[參考摺疊規則](https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers)。 – vsoftco

1

T&&可以被稱爲universal/forwarding參考。
參考塌陷規則:

  1. 甲& &成爲A &
  2. 甲& & &成爲A &
  3. 甲& & &成爲A &
  4. 甲& & & &成爲A & &

template<typename T> void foo(T&&);

在此,以下適用:

  1. 當FOO上稱爲類型A的左值,則T解析爲&和 因此通過上述參考摺疊規則,參數類型 實際上變成A &。
  2. 當在類型A的右值上調用foo時,T解析爲A,並且 因此參數類型變爲A & &。

在你的情況:

template<class T> void f_(T&& arg); 
f_(block); //case 1 
f_(MemoryBlock()); //case 2 

在情況1:
T = MemoryBlock中&則T & &換成T & & & ==>給出Ť&
在情況2:
T = MemoryBlock然後T & &變成T & & ==>給牛逼& &

對於這兩種情況下

template<class T> void f_(T&& arg) 

是編譯器,因此,反而採取其

template<class T> 
void f_(const T& arg)