2013-09-23 32 views
5
#include<iostream> 
using namespace std; 

int &fun() 
{ 
    static int x = 10; 
    return x; 
} 
int main() 
{ 
    fun() = 30; 
    cout << fun(); 
    return 0; 
} 

Hàm fun() trả về giá trị theo tham chiếu nhưng trong phương thức main() tôi gán một số hàm int. Lý tưởng nhất, một trình biên dịch sẽ hiển thị một lỗi như lvalue yêu cầu nhưng trong trường hợp trên chương trình hoạt động tốt. Tại sao nó như vậy?Gán giá trị cho hàm trả về tham chiếu

+5

Trở tham chiếu đến biến tĩnh là vế trái (và hợp pháp). Tại sao bạn nghĩ rằng nên có bất kỳ lỗi nào? –

+0

Giá trị trả về từ 'fun()' là 'int &' là 'lvalue' –

+0

Đánh giá tham chiếu * kết quả trong * một giá trị. –

Trả lời

7

Ngôn ngữ lỏng lẻo và cẩu thả để nói "hàm trả về thứ gì đó". Đó là OK như là một viết tắt nếu bạn biết làm thế nào để làm việc với điều đó, nhưng trong trường hợp này bạn bị lẫn lộn.

Cách chính xác hơn để suy nghĩ về điều này là bạn đánh giá biểu thức cuộc gọi hàm. Làm như vậy sẽ cung cấp cho bạn giá trị . Giá trị là giá trị rvalue hoặc một giá trị (chi tiết modulo).

Khi T là loại đối tượng và bạn đánh giá hàm có kiểu trả về T, bạn nhận được giá trị loại T là giá trị. Mặt khác, nếu hàm có kiểu trả về là T &, bạn nhận được một giá trị loại T là một giá trị (và giá trị là thứ được liên kết với tham chiếu trong câu lệnh return).

+2

Lưu ý rằng 'f() = value' là lỗi chỉ khi' f() 'đánh giá thành một giá trị của * kiểu dựng sẵn *, nếu không thì sẽ ổn nếu nó đánh giá rvalue của kiểu lớp. (lvalue trong mọi trường hợp là tốt rồi) – Nawaz

3

Trả lại tham chiếu khá hữu ích.

Ví dụ: đó là những gì std::map::operator[] thực hiện. Và tôi hy vọng bạn thích khả năng viết my_map[key] = new_value;.

Nếu chức năng thông thường (phi toán tử) trả về một tham chiếu thì bạn có thể gán cho nó và tôi không thấy bất kỳ lý do nào mà điều này sẽ bị cấm.

Bạn có thể ngăn bài tập bằng cách trả lại const X& hoặc bằng cách trả lại X thay vào đó nếu bạn thực sự muốn.

2

Nó hoạt động vì kết quả của hàm đó một giá trị. Tham chiếu là giá trị. Về cơ bản, trong toàn bộ điểm trả về một tham chiếu không const từ một hàm là có thể gán cho nó (hoặc thực hiện các sửa đổi khác của đối tượng được tham chiếu).

1

Giá trị L là giá trị định vị. Nó có nghĩa là nó có địa chỉ. Một tham chiếu rõ ràng có một địa chỉ. Các vế trái cần bạn có thể nhận được nếu bạn quay lại từ vui nhộn() theo giá trị:

#include<iostream> 
using namespace std; 

int fun() 
{ 
    static int x = 10; 
    return x; 
} 
int main() 
{ 
    fun() = 30; 
    cout << fun(); 
    return 0; 
} 
2

Ngoài câu trả lời khác, hãy xem xét đoạn mã sau:

SomeClass& func() { ... } 

func().memberFunctionOfSomeClass(value); 

Đây là một điều hoàn toàn tự nhiên để làm, và tôi sẽ rất ngạc nhiên nếu bạn mong đợi trình biên dịch cung cấp cho bạn một lỗi về điều này.

Bây giờ, khi bạn viết some_obj = value; điều thực sự xảy ra đằng sau hậu trường là bạn gọi some_obj.operator =(value);. Và operator =() chỉ là một chức năng thành viên khác của lớp học của bạn, không khác với memberFunctionOfSomeClass().

Tất cả trong tất cả, nó nắm tới:

func() = value; 
// equivalent to 
func().operator =(value); 
// equivalent to 
func().memberFunctionOfSomeClass(value); 

Tất nhiên điều này là quá đơn giản hóa, và ký hiệu này không áp dụng cho xây dựng trong các loại như int (nhưng các cơ chế tương tự được sử dụng).

Hy vọng điều này sẽ giúp bạn hiểu rõ hơn những gì người khác đã giải thích dưới dạng lvalue.

1

Tôi đã được buffled bởi mã tương tự quá - tại nắm tay. Đó là "tại sao tôi gán giá trị cho một cuộc gọi hàm và tại sao trình biên dịch lại hài lòng với nó?" Tôi tự hỏi. Nhưng khi bạn nhìn vào những gì xảy ra "đằng sau", nó có ý nghĩa.


Như cpp và những người khác poined ra, lvalues ​​là "vị trí bộ nhớ" có địa chỉ và chúng ta có thể gán giá trị cho họ. Bạn có thể tìm thêm thông tin về chủ đề của các giá trị và giá trị on the internet.

Khi chúng ta nhìn vào các chức năng:

int& fun() 
{ 
    static int x = 10; 
    return x; 
} 

Tôi di chuyển các & to go, do đó, nó rõ ràng hơn chúng ta đang trở về một tham chiếu đến int.
Chúng tôi thấy chúng tôi có x, đó là lvalue - địa chỉ này có địa chỉ và chúng tôi có thể gán cho nó. Nó cũng là tĩnh, làm cho nó đặc biệt - nếu nó không tĩnh, tuổi thọ (phạm vi) của biến sẽ kết thúc với ngăn xếp thư giãn khi rời khỏi hàm và sau đó tham chiếu có thể trỏ tới bất kỳ lỗ đen nào tồn tại trong vũ trụ. Tuy nhiên, x là tĩnh, nó sẽ tồn tại ngay cả sau khi chúng ta rời khỏi hàm (và khi chúng ta quay lại hàm) và chúng ta có thể truy cập nó bên ngoài hàm.

Chúng tôi đang trả lại tham chiếu đến một int và kể từ khi chúng tôi trả lại x, tham chiếu đến x. Sau đó, chúng tôi có thể sử dụng tham chiếu để thay đổi x bên ngoài chức năng. Vì vậy:

int main() 
{ 
    fun(); 

Chúng tôi chỉ gọi hàm. Biến x (trong phạm vi chức năng thú vị) được tạo, nó có giá trị là 10 được gán. Đó là địa chỉ và giá trị tồn tại ngay cả sau khi chức năng còn lại - nhưng chúng ta không thể sử dụng giá trị của nó, vì chúng ta không có địa chỉ của nó.

fun() = 30; 

Chúng tôi gọi hàm và sau đó thay đổi giá trị của x. Giá trị x được thay đổi thông qua tham chiếu được hàm trả về. LƯU Ý: chức năng được gọi là đầu tiên và chỉ sau khi cuộc gọi chức năng được hoàn thành, sau đó, việc chuyển nhượng diễn ra.

int& reference_to_x = fun(); // note the & 

Bây giờ chúng ta (cuối cùng) giữ tham chiếu đến x trả về bởi hàm. Bây giờ chúng tôi có thể thay đổi x mà không cần gọi hàm trước.(reference_to_x lẽ sẽ có địa chỉ giống như x có bên trong hàm vui vẻ)

int copy_of_x = fun(); // no & this time 

Lần này chúng tôi tạo new int và chúng tôi chỉ cần sao chép giá trị của x (thông qua các tài liệu tham khảo). Int mới này có địa chỉ riêng của nó, nó không trỏ đến x như reference_to_x là.

reference_to_x = 5; 

Chúng tôi giao x giá trị 5 thông qua các tài liệu tham khảo, và chúng tôi thậm chí không gọi hàm. copy_of_x không thay đổi.

copy_of_x = 15; 

Chúng tôi đã thay đổi int mới để đánh giá 15. Các x không được thay đổi, kể từ copy_of_x có địa chỉ riêng của mình.

} 


Như 6502 và những người khác chỉ ra, chúng tôi sử dụng phương pháp tương tự với trở về tài liệu tham khảo rất nhiều với container và ghi đè tùy chỉnh.

std::map<std::string, std::string> map = {}; 

map["hello"] = "Ahoj"; 
// is equal to 
map.operator[]("hello") = "Ahoj"; // returns reference to std::string 
// could be done also this way 
std::string& reference_to_string_in_map = map.operator[]("hello"); 
reference_to_string_in_map = "Ahoj"; 

Chức năng bản đồ chúng tôi sử dụng có thể có tuyên bố như thế này:

std::string& map::operator[](const std::string& key); // returns reference 

Chúng tôi không có địa chỉ để các chuỗi chúng ta "lưu trữ" trên bản đồ, vì vậy chúng ta gọi là chức năng ghi đè này của bản đồ , chuyển khóa đó để bản đồ biết chuỗi nào chúng ta muốn truy cập và nó trả về cho chúng ta tham chiếu đến chuỗi đó, mà chúng ta có thể sử dụng để thay đổi giá trị. CHÚ Ý: một lần nữa chức năng được gọi là đầu tiên và chỉ sau khi nó được hoàn thành (bản đồ tìm thấy chuỗi chính xác và tham chiếu trả về nó) nhiệm vụ xảy ra. Nó giống như với niềm vui() = 10, chỉ xinh đẹp hơn ...

Hope this helps bất cứ ai vẫn woudn't hiểu tất cả mọi thứ, ngay cả sau khi đọc câu trả lời khác ...