2013-07-31 46 views
11

tôi đã được học đi ngang qua tham khảo, và đây là bài kiểm tra tôi đã làm:Vượt qua tham chiếu, tham chiếu liên tục, tham chiếu rvalue, hoặc tham chiếu rvalue không đổi?

#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; 
} 

Và đây là kết quả tôi nhận được:

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 

Câu hỏi của tôi là:

  1. Nếu chúng ta chuyển một biểu thức không đổi, nó luôn kích hoạt tham chiếu rvalue không liên tục? Trong điều kiện nào nó sẽ kích hoạt tham chiếu rvalue liên tục (và tại sao s6 không phải là trigging nó?)

  2. Tại sao tham chiếu không liên tục và tham chiếu rvalue liên tục là bất hợp pháp?

  3. Tôi dự kiến ​​không thể thay đổi s3, nhưng tại sao b trong phạm vi bên trong có thể thay đổi s3? Nếu gán một đối tượng mới s3 cho b là gán một tham chiếu mới, tại sao khi tôi gán s4 cho nó và s3 đã thay đổi và s4 rỗng sau đó?

Xin lỗi vì đã đặt quá nhiều câu hỏi ... Tôi sẽ tăng điểm khi tất cả câu hỏi được trả lời :) Tham chiếu chỉ mang đến sự nhầm lẫn của tôi từ con trỏ đến một cấp độ hoàn toàn mới.


Tôi không biết cách tăng điểm ... vì vậy sẽ chờ 2 ngày cho đến khi đủ điều kiện nhận tiền thưởng, sau đó chọn câu trả lời.

+7

Không có tham chiếu * tham chiếu * trong C++. Cú pháp '&&' hoặc là tham chiếu đến một tham chiếu rvalue hoặc một tham chiếu phổ quát (trong các khuôn mẫu). – dyp

+0

Bằng cách tham chiếu tham chiếu bạn có nghĩa là tham chiếu rvalue. Tiêu chuẩn cụ thể gọi ra tham chiếu đến tham chiếu không được phép. Chỉ là FYI. C++ 11 § 8.3.2, p5: * "Sẽ không có tham chiếu đến tham chiếu, không có mảng tham chiếu, và không có con trỏ tới tham chiếu. ..." * Viết tắt cho .. well .. ngắn gọn. – WhozCraig

+0

OK Tôi đã sửa chúng. – texasbruce

Trả lời

11

Đầu tiên mã

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. 
} 

Sau đó các câu hỏi:

Nếu chúng ta vượt qua một biểu hiện liên tục, nó luôn luôn gây nên không thường xuyên rvalue tham khảo? Dưới những điều kiện nó sẽ kích hoạt liên tục rvalue tham chiếu (và tại sao s6 không được dọn dẹp và nó?)

đối tượng hiện tại sẽ vượt qua như string& hoặc const string& tùy thuộc vào nếu họ đang const hay không. Chúng cũng có thể được sao chép ở dạng string. Các thời gian sẽ chuyển thành string&&, nhưng cũng có thể được sao chép ở dạng string. Có cách kích hoạt const string&&, nhưng không có lý do nào để làm như vậy, vì vậy không quan trọng. They're shown here.

Tại sao tham chiếu không liên tục và tham chiếu rvalue không đổi là bất hợp pháp?

Tiêu chuẩn cụ thể nói rằng chỉ const string&string&& sẽ kéo dài cuộc sống của temporaries, mặc dù tôi không chắc chắn lý do tại sao họ đã không còn đề cập đến string&const string&&.

Tôi dự kiến ​​không thể thay đổi s3, nhưng tại sao b trong phạm vi bên trong có thể thay đổi s3? Nếu gán một đối tượng mới s3 cho b là gán một tham chiếu mới, tại sao khi tôi gán s4 cho nó và s3 đã thay đổi và s4 rỗng sau đó?

Bạn đã khởi tạo b làm tham chiếu đến s3. Không phải là bản sao mà là một tham chiếu. Điều đó có nghĩa là b hiện đề cập đến s3mãi mãi, bất kể số là gì. khi bạn nhập b = "b changed after assigning s3\n";, chính xác là giống như s3 = "b changed after assigning s3\n";. Khi bạn nhập b = s4;, điều đó hoàn toàn giống với s3 = s4. Đó là những gì một tài liệu tham khảo. Chúng không thể được "reseated".

+4

Tôi không cảm thấy như thế này sẽ được coi là một nhận xét mang tính xây dựng theo quy tắc SOF nhưng tôi cần phải nói rằng tôi thích học hỏi và học lại những điều thông qua bạn là câu trả lời @Mooing Duck. Bạn giải thích mọi thứ một cách đầy đủ và theo cách có ý nghĩa với tôi. Tôi biết tôi không phải là người duy nhất cảm thấy theo cách này và nếu công việc của tôi không chặn tính năng Trò chuyện của SOF, tôi đã làm điều này trong một cài đặt thích hợp hơn. Nhưng cảm ơn vì sự giúp đỡ bạn đã cho tôi, cho dù đó là trực tiếp hay không. Email của tôi trong tiểu sử của tôi và tôi muốn trò chuyện với bạn về việc viết mã đôi khi nếu bạn muốn. :) – Dan

+1

* "Có nhiều cách để kích hoạt' const string && ', nhưng không có lý do gì để làm như vậy, vì vậy nó không quan trọng." * Người đàn ông, đó là một letdown. Bạn có phiền không? :) – Mehrdad

+2

@Mehrdad: Đã chỉnh sửa hai cách mà ngay lập tức nghĩ đến câu hỏi, mặc dù tôi chắc chắn có những người khác. gọi 'std :: move' trên biến const có thể hoạt động chẳng hạn. Tất cả ba cách đều khá rõ ràng, có thể bạn sẽ không tìm thấy chúng một cách vô tình –

6

giá trị có thể liên kết với tham chiếu rvalue và tham chiếu giá trị const, ví dụ:

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

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

Nhưng rvalue không thể liên kết với tham chiếu không phải lvalue. Độ phân giải quá tải thích thời gian ràng buộc để rvalue-refs hơn ràng buộc chúng với const lvalue refs:

lvalues ​​chỉ có thể liên kết với tham chiếu lvalue. Lưu ý, tuy nhiên, const hạn chế này:

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` 

Bạn có thể std::move lvalues ​​để ràng buộc họ rvalue tài liệu tham khảo cũng như:

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

này được vì khái niệm của một rvalue là bạn có thể tái chế nội dung của nó, ví dụ yêu cầu quyền sở hữu bộ nhớ mà nó đã cấp phát động. Điều này có thể nguy hiểm nếu bạn vẫn có quyền truy cập vào đối tượng sau khi thao tác, do đó yêu cầu std::move rõ ràng cho các giá trị.


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; 
} 

Nếu chúng ta vượt qua một biểu hiện liên tục, nó luôn luôn gây nên không thường xuyên rvalue tham khảo? Trong điều kiện nào nó sẽ kích hoạt tham chiếu rvalue liên tục (và tại sao s6 không phải là trigging nó?)

Một biểu thức hằng số duy nhất có thể chứa (vế trái-to-rvalue chuyển đổi của) đối tượng tuyên bố hoặc constexpr hoặc const, hoặc là tạm thời, đó là rvalues. Do đó, AFAIK, một biểu thức liên tục không thể sinh ra một giá trị không phải là const.


Tại sao tham chiếu không liên tục và liên tục rvalue tham chiếu là bất hợp pháp?

Cả hai đều được cho phép. Mặc dù const rvalue refs không có ý nghĩa gì đối với tôi, bạn cũng có thể sử dụng const lvalue-refs.


tôi mong đợi một không thể thay đổi s3, nhưng tại sao b trong phạm vi bên trong có thể thay đổi s3? Nếu gán một đối tượng mới s3 cho b là gán một tham chiếu mới, tại sao khi tôi gán s4 cho nó và s3 đã thay đổi và s4 rỗng sau đó?

Tôi nghĩ bạn đang bối rối về sự khác biệt giữa việc khởi tạo tham chiếu và gán cho tên bạn đã khai báo làm tham chiếu.

3

Nếu chúng ta chuyển biểu thức không đổi, nó luôn kích hoạt tham chiếu rvalue không liên tục? Trong điều kiện nào nó sẽ kích hoạt tham chiếu rvalue liên tục (và tại sao s6 không phải là trigging nó?)

Đối với một biểu thức không đổi? Không ai. Lần duy nhất thứ gì đó sẽ liên kết với const&& sẽ là nếu nó đã là const. Và thậm chí sau đó, điều đó sẽ yêu cầu một diễn viên rõ ràng nếu nó là một biến (xem bên dưới).

Tại sao tham chiếu không liên tục và tham chiếu rvalue không đổi là bất hợp pháp?

tôi sẽ giả sử bạn đang nói về những:

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

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

Đầu tiên là bất hợp pháp vì "" không phải là một biến std::string. Do đó, tòa nhà phải xây dựng tạm thời std::string từ "". s là tham chiếu không const đối với chuỗi hiện tại biến. Bạn không thể tham chiếu không tham chiếu đến tạm thời, vì tạm thời không phải là biến.

Thứ hai là bất hợp pháp vì (bỏ qua thực tế là s1 không tồn tại) C++ không cho phép bạn nhận tham chiếu giá trị r cho biến mà không cần chuyển đổi rõ ràng. Đây là những gì std::move dành cho. const string &&s{std::move(s3)} hoạt động tốt.

Tôi dự kiến ​​không thể thay đổi s3, nhưng tại sao b trong phạm vi bên trong có thể thay đổi s3? Nếu gán một đối tượng mới s3 cho b là gán một tham chiếu mới, tại sao khi tôi gán s4 cho nó và s3 đã thay đổi và s4 rỗng sau đó?

Trước tiên, bạn có thể thay đổi s3 tốt.b là tham chiếu đến s3; chúng là hai tên cho cùng một đối tượng. Đối với phần còn lại, bạn không thể thay đổi đối tượng được tham chiếu bởi b sau khi b được tạo. b bắt đầu tham chiếu s3, do đó, nó sẽ luôn luôn làm như vậy. Do đó b = s4 có nghĩa là sao chép s4 vào bất kỳ đối tượng nào được tham chiếu bởi b, là s3.

s4 trống sau đó vì nó là luôn trống. Bạn đã gán chuỗi trống cho nó. Vì vậy, nó trống rỗng.

2

Bạn nên ngừng nghĩ đến Foo&& làm tham chiếu giá trị. Hãy suy nghĩ thay vì những thứ liên kết với.

Chức năng chụp Foo&& sẽ chỉ liên kết với tạm thời Foo s hoặc Foo s được đánh dấu là tạm thời.

Dấu tạm thời này không kéo dài.Nếu bạn có một biến số Foo&& foo và bạn sử dụng nó, nó không được đánh dấu là tạm thời tại thời điểm sử dụng. Việc đánh dấu một điều gì đó tạm thời chỉ có thể xảy ra ngay lập tức - bằng một hàm trả về một số Foo&& hoặc bằng cách trả về một số vô danh Foo, trong đó sử dụng ngay lập tức, được coi là tạm thời.

Những cách tiêu chuẩn để đánh dấu dữ liệu tạm thời là (A) là một ví dụ mang tính chất của Foo đó chỉ là tạm thời, (B), bạn gọi std::move trên một thể hiện của Foo, (C), bạn gọi std::forward<Foo> trên một thể hiện của Foo.

Trong thực tế, && được sử dụng cả bởi những gì được gọi là tham chiếu chung và theo tham chiếu bạn muốn liên kết với thời gian. Trong một bối cảnh khấu trừ loại, tham chiếu giá trị lvalue có thể được lưu trữ trong một T&& bằng cách làm cho T thành Foo& - tham chiếu giá trị "thắng" trong tham chiếu rvalue. Đây là tình huống bạn cần gọi std::forward để di chuyển có điều kiện.

Tóm lại: có bốn điểm hợp lệ phổ biến để sử dụng &&.

  • Khi bạn lấy một đối số bạn muốn được chuyển từ trong danh sách đối số hoặc hàm.
  • Khi bạn đang sử dụng chuyển tiếp hoàn hảo và kỹ thuật tham chiếu chung trong các đối số của hàm template.
  • Khi bạn đang thực hiện chuyển tiếp thông số được chuyển tiếp hoàn hảo đến giá trị trả lại.
  • Khi bạn đang thực hiện kỹ thuật tham chiếu chung để tạo tham chiếu đến hàm có thể tạm thời trong phạm vi chức năng (Ví dụ: for(auto&& i:x)).

Khi sử dụng biến có tên là &&, hoạt động gần như chính xác như biến số & hoặc const &. Để sử dụng nó theo cách được coi là tạm thời, bạn cần phải std::move hoặc trong ngữ cảnh tham chiếu chung, sử dụng std::forward với điều kiện std::move.

4

Chỉ cần trả lời phần này:

Dưới những điều kiện nó sẽ kích hoạt liên tục rvalue tham chiếu

Hằng số quá tải rvalue tham chiếu sẽ được sử dụng khi bạn gọi nó với một rvalue của hằng số loại:

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)); 

Chức năng chung dùng const X&& là vô dụng, vì bạn không thể di chuyển từ một hằng số. Chúng có thể hữu ích như các chức năng đã xóa, để ngăn chặn các cuộc gọi nhất định từ biên dịch.

Các vấn đề liên quan