2013-07-31 74 views
11

我是學引用傳遞,這裏是測試我所做的:通過引用,常量引用,右值引用或常量右值引用傳遞?

#include <iostream> 

using namespace std; 

int i = 0; 

//If this is uncommented, compiler gives ambiguous definition error. 
//void paramCheck (string s) { 
// cout << ++i << ". Param is var.\n"; 
//} 

void paramCheck (const string& s) { 
    cout << ++i << ". Param is const ref.\n"; 
} 

void paramCheck (string& s) { 
    cout << ++i << ". Param is non-const ref.\n"; 
} 

void paramCheck (const string&& s) { 
    cout << ++i << ". Param is const rvalue-reference.\n"; 
} 

void paramCheck (string&& s) { 
    cout << ++i << ". Param is non-const rvalue-reference.\n"; 
} 


int main(int argc, char **argv) { 
    //Function call test 
    paramCheck(""); 

    paramCheck(string{""}); 

    string s3{""}; 
    paramCheck(s3); 

    const string s4{""}; 
    paramCheck(s4); 

    //Illegal 
    //string& s{""}; 
    //paramCheck(s); 

    const string& s5{s3}; 
    paramCheck(s5); 

    string&& s6{""}; 
    paramCheck(s6); 

    //Illegal 
    //const string&& s{s1}; 
    //onstFP(s); 

    //Reference test 
    string a = s3; 
    a = "a changed s3"; 
    cout << s3; 

    { 
    string& b = s3; 
    b = "b changed after assigning s3\n"; 
    cout << "s3 is now " <<s3; 

    b = s4; 
    b = "b changed after assigning s4\n"; 
    cout << "s3 is now " <<s3; 
    cout << "s4 is now " <<s4; 
    } 

    cin.get(); 
    return 0; 
} 

,這裏是結果我得到:

1. Param is non-const rvalue-reference. 
2. Param is non-const rvalue-reference. 
3. Param is non-const ref. 
4. Param is const ref. 
5. Param is const ref. 
6. Param is non-const ref. 
s3 is now b changed after assigning s3 
s3 is now b changed after assigning s4 
s4 is now 

我的問題是:

  1. 如果我們傳遞一個常量表達式,它總是觸發非常量右值引用?在什麼情況下它會觸發恆定的右值參考(爲什麼s6不會觸發它?)

  2. 爲什麼非常量引用和常量右值引用是非法的?

  3. 我期望a不能改變s3,但爲什麼在內部範圍b可以改變s3?如果給b分配一個新的對象s3是分配一個新的引用,爲什麼當我將s4賦值給s3並且s3被更改並且s4之後是空的?

對不起,問太多的問題......我會增加點時,所有的問題都回答:)參考只是帶來了指針我的困惑到一個全新的水平。


我不知道如何增加點...所以將等待2天,直到有資格獲得賞金,然後選擇答案。

+7

在C++中沒有引用引用*。 '&&'語法是指右值引用或通用引用(在模板中)。 – dyp

+0

通過引用您引用的意思是右值引用。該標準特別呼籲引用引用是不允許的。只是FYI。 C++ 11§8.3.2,p5:*「應該沒有對引用的引用,沒有引用數組,並且沒有指向引用的指針......」*簡寫爲..以及簡潔。 – WhozCraig

+0

好,我糾正了它們。 – texasbruce

回答

11

首先代碼

paramCheck(""); //constructs a temporary. temporaries bind to `string&&` 
paramCheck(string{""}); //constructs a temporary. temporaries bind to `string&&` 
string s3{""}; 
paramCheck(s3); //passes a reference to an existing string: `string&` 
const string s4{""}; 
paramCheck(s4); //passes a reference to an existing string+const: `const string&` 
//Illegal 
//string& s{""}; //cannot assign a temporary to a non-const l-reference 
       //what would s refer to when the temporary "dies"? 
       //`const string&` would have worked though 
//paramCheck(s); //passes a reference to an existing string+const: `const string&` 
const string& s5{s3}; //s5 is s3, but with `const`. 
paramCheck(s5); //passes a reference to an existing string+const: `const string&` 
string&& s6{""}; //r-references extend the life of temporaries. 
paramCheck(s6); //passes a reference to an existing strong: `string&` 
//const string&& s{s1}; //temporaries can be extended by `T&&` or `const T&` only. 

//Reference test 
string a = s3; //a is a _copy_ of s3 
a = "a changed s3"; //so changing the copy doesn't effect the origional. 
cout << s3; //s3 is still blank, it hasn't changed. 

{ 
string& b = s3; //b isn't really a "reference" to `s3`". `b` _IS_ `s3`. 
b = "b changed after assigning s3\n"; //since `b` IS `s3`, this changes `s3`. 
cout << "s3 is now " <<s3; 

b = s4; //`b` _IS_ `s3`, so you just changed `s3` again. 
b = "b changed after assigning s4\n"; 
cout << "s3 is now " <<s3; 
cout << "s4 is now " <<s4; //s4 is still blank, it hasn't changed. 
} 

然後提問:

如果我們傳遞一個常量表達式,它總是會觸發非恆右值引用?在什麼條件下會觸發恆右值引用(爲什麼S6是不是trigging嗎?)

爲依據,如果他們const或不string&const string&現有的對象將會通過。它們也可以複製爲string。臨時文件將以string&&的格式傳遞,但也可以複製爲string。有方式來觸發const string&&,但沒有理由這樣做,所以沒關係。 They're shown here

爲什麼非常量引用和常量右值引用是非法的?

標準明確指出,只有const string&string&&將延長臨時工的生活,雖然我不能肯定他們爲什麼不還提到string&const string&&

我預期不能改變S3,但爲什麼b。在內部範圍內可以改變S3?如果給b分配一個新的對象s3是分配一個新的引用,爲什麼當我將s4賦值給s3並且s3被更改並且s4之後是空的?

您初始化爲b作爲參考s3。不是副本,而是參考。這意味着b現在指s3永遠,不管是什麼。當你輸入b = "b changed after assigning s3\n";時,這與s3 = "b changed after assigning s3\n";完全一樣。當您輸入b = s4;時,與s3 = s4完全相同。這是一個參考。他們不能被「重新安置」。

+4

根據SOF規則,我不認爲這會被認爲是一種建設性的評論,但我需要說我喜歡通過你的回答學習和重新學習東西@Mooing Duck。你以一種對我來說合情合理的方式充分解釋事情。我知道我不是唯一一個這樣認爲的人,如果我的工作沒有阻止SOF的聊天功能,我會在更合適的環境下做到這一點。但是謝謝你給我的幫助,不管是否直接。我的個人資料中包含我的電子郵件,如果您願意,可以隨時與您聊聊編碼問題。 :) – Dan

+1

*「有些方法可以觸發'const string &&',但是沒有理由這樣做,所以沒關係。」*男人,這真是令人失望。你介意怎麼解釋? :) – Mehrdad

+2

@Mehrdad:編輯了這兩個方式,立即想到問題中,但我確定有其他人。在const變量上調用'std :: move'可能適用於實例。所有三種方式都很明確,你可能不會意外發現它們 –

6

rvalues可以綁定到右值引用和const左值引用,例如,

void foo(const string&); 
void bar(string&&); 

foo(string{}); 
bar(string{}); 

但是右值不能綁定到非常量左值引用。重載決策更喜歡結合的臨時到右值,裁判在他們的結合爲const左值裁判:

void foo(const string&); 
void foo(string&&); 

foo(string{});   // will call the second overload 

左值只能綁定到左值引用。但是請注意,這const限制了這一點:

const string do_not_modify_me; 
string& modify_me = do_not_modify_me; // not allowed, because `do_not_modify_me` 
modify_me += "modified";    // shall not be modified: declared as `const` 

可以std::move左值來約束他們的右值引用,以及:

string s; 
string&& r = std::move(s); 

這是因爲右值的概念是,你可以回收其內容,例如聲明它已動態分配的內存的所有權。如果您在操作後仍然可以訪問該對象,則這可能很危險,因此左值需要明確的std::move


paramCheck("");   // a string literal is an lvalue (!) 
         // see [expr.prim.general]/1 
         // but it is implicitly converted to a `std::string`, 
         // creating a `string` temporary, a rvalue 

paramCheck(string{""}); // a temporary is an rvalue 

string s3{""}; 
paramCheck(s3);   // the variable `s3` is an lvalue of type `string` 

const string s4{""}; 
paramCheck(s4);   // the variable `s4` is an lvalue of type `const string` 

//Illegal 
//string& s{""};  // can't bind a temporary to a non-const lvalue ref 
//paramCheck(s); 

const string& s5{s3}; 
paramCheck(s5);   // the variable `s5` is a lvalue of type `const string` 

string&& s6{""};  // binding a temporary to a rvalue-ref (allowed) 
paramCheck(s6);   // the variable `s6` is an lvalue (!) - it has a name 

//Illegal 
//const string&& s{s1}; // `s1` has not been declared 
//onstFP(s); 

//Reference test 
string a = s3;   // copy the contents of `s3` to a new string `a` 
a = "a changed s3";  // overwrite contents of `a` 
cout << s3; 

{ 
string& b = s3;   // `b` refers to `s3` now (like an alias) 
b = "b changed after assigning s3\n"; 
cout << "s3 is now " <<s3; 

b = s4;     // copy the contents of `s4` to `b` (i.e. to `s3`) 
b = "b changed after assigning s4\n"; 
cout << "s3 is now " <<s3; 
cout << "s4 is now " <<s4; 
} 

如果我們傳遞一個常量表達式,它總是會觸發非恆右值引用?在什麼情況下,它會觸發常數右值參考(爲什麼s6不會觸發它?)

常量表達式只能包含的(左值到右值轉換)對象聲明爲constexprconst,或臨時變量,其是右值。因此,AFAIK,常量表達式不能產生非常量左值。


爲什麼不恆定的參考和恆右值引用是非法的?

兩者都是允許的,實際上。儘管const rvalue參考對我沒有任何意義,但您也可以使用const lvalue-refs。


我預期不能改變S3,但爲什麼b。在內部範圍內可以改變S3?如果給b分配一個新的對象s3是分配一個新的引用,爲什麼當我將s4賦值給s3並且s3被更改並且s4之後是空的?

我覺得你對引用的初始化和賦值給你聲明的名字的區別感到困惑。

3

如果我們傳遞一個常量表達式,它總是觸發非常量右值引用?在什麼情況下它會觸發恆定的右值參考(爲什麼s6不會觸發它?)

對於一個常量表達式?沒有。如果它已經是const,那麼唯一的一次綁定到const&&。即便如此,如果它是一個變量,則需要進行明確的轉換(見下文)。

爲什麼非常量引用和常量右值引用是非法的?

我假設你正在談論這些:

//string& s{""}; 
//paramCheck(s); 

//const string&& s{s1}; 
//onstFP(s); 

首先是非法的,因爲""不是std::string變量。因此,它必須從""構建一個std::string臨時。 s是對現有字符串變量的非const引用。由於臨時變量不是變量,因此不能將非const引用指向臨時變量。

二是非法的,因爲(忽略一個事實,即s1不存在)C++不允許你得到的r值參考的變量沒有明確轉換。這是std::move的用途。 const string &&s{std::move(s3)}工作得很好。

我預期不能改變S3,但爲什麼b。在內部範圍內可以改變S3?如果給b分配一個新的對象s3是分配一個新的引用,爲什麼當我將s4賦值給s3並且s3被更改並且s4之後是空的?

首先,你可以改變s3就好了。b參考號s3;他們是同一個對象的兩個名字。至於其他方面,在創建b之後,您無法更改b引用的對象。 b開始引用s3,因此它會總是這樣做。因此b = s4意味着將s4複製到b引用的任何對象,即s3

s4之後爲空,因爲它是總是爲空。您將空字符串分配給它。所以它是空的。

2

您應該停止將Foo&&作爲右值引用。思考一下事情的結合。

Foo&&作爲參數的函數只能與暫時的Foo s或Foo s綁定。

此臨時標記不會持續。如果您有一個變量Foo&& foo,並且您使用該變量,則在使用時它不會標記爲臨時值。將某些事物標記爲臨時事件只能立即發生 - 通過函數返回Foo&&,或者返回一個匿名Foo,立即使用該臨時事件。

標記數據作爲臨時是(A)它是Foo匿名實例是暫時的,(B)你的Foo一個實例稱爲std::move,(C)所述的標準方法,你上的一個實例稱爲std::forward<Foo>Foo

在實踐中,&&既被稱爲通用引用,也被用於綁定到臨時對象的引用。在類型推導上下文中,通過將T設置爲Foo&-左值引用「勝過」右值引用,左值引用可以存儲在T&&中。在這種情況下,您需要撥打std::forward纔能有條件地轉移。

總之:有四個常用的有效點可以使用&&

  • 當你想從參數列表中移出參數時,
  • 當您在template函數的參數中使用完美轉發和通用參考技術時。
  • 當您將完美的轉發參數傳遞給返回值時。
  • 當您正在執行通用參考技術以在函數作用域中創建對maybe-a-temporary的引用時(例如,for(auto&& i:x))。

當使用名爲&&變量,它的行爲幾乎完全一樣&const &變量。爲了以臨時性的方式使用它,您需要std::move或在通用參考環境中使用std::forward以有條件地std::move

4

只是爲了回答這個問題的一部分:

在什麼條件下會觸發恆右值引用

當你不斷右值稱之爲恆右值引用過載將使用類型:

void paramCheck (const string&& s) { 
    cout << ++i << ". Param is const rvalue-reference.\n"; 
} 

const std::string functionThatReturnsConstantRvalue() { return ""; } 

// ... 

paramCheck(functionThatReturnsConstantRvalue()); 

const std::string s; 
paramCheck(std::move(s)); 

在服用const X&&一般功能都是沒用的,因爲你不能從一個恆定的移動。它們可以用作已刪除的函數,以防止編譯某些調用。