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

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

Функция fun() возвращает значение по ссылке, но в методе main() Я назначаю некоторый int для работы. В идеале, компилятор должен показывать ошибку, такую ​​как lvalue, но в этом случае программа работает нормально. Почему это так?Назначение значения возвращаемой функции

+5

Возвращаясь ссылки на статические переменный-значении (и юридическое). Почему вы думаете, что должны быть какие-то ошибки? –

+0

Возвращаемое значение из 'fun()' is 'int &' которое является 'lvalue' –

+0

Оценка ссылки * приводит к * значению l. –

ответ

7

Это свободный и неряшливый язык, чтобы сказать «функция возвращает что-то». Это нормально, как стенография, если вы знаете, как с этим работать, но в этом случае вы путаетесь.

Более правильный способ подумать о том, что вы оцениваете выражение вызова функции. Это дает вам значение . Значение - это значение rvalue или lvalue (по модулю).

Когда T является типом объекта, и вы оцениваете функцию с возвратным типом T, вы получаете значение типа T, которое является значением rvalue. С другой стороны, если функция имеет тип возвращаемого значения T &, вы получаете значение типа T, которое является значением lvalue (а значение привязывается к ссылке в заявлении return).

+2

Обратите внимание, что 'f() = value' является ошибкой только в том случае, если' f() 'оценивает значение r встроенного типа *, в противном случае это нормально, если оно оценивает значение rvalue типа класса. (lvalue в любом случае уже хорошо) – Nawaz

3

Возвращение справки весьма полезно.

Например, это то, что std::map::operator[]. И я надеюсь, вам понравится возможность писать my_map[key] = new_value;.

Если регулярная функция (не оператор) возвращает ссылку, тогда ее можно назначить, и я не вижу причин, по которым это должно быть запрещено.

Вы можете предотвратить назначение, вернув const X& или вернув X вместо этого, если вы действительно этого хотите.

2

Это работает, потому что это результат lvalue. Ссылки - lvalues. В принципе, во всей точке возврата неконстантная ссылка от функции должна быть в состоянии назначить ей (или выполнить другие модификации ссылочного объекта).

1

L-значение Локатор-значение. Это означает, что у него есть адрес. Ссылка явно имеет адрес. именующее требуется вы можете получить, если вы вернетесь из удовольствия() по значению:

#include<iostream> 
using namespace std; 

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

В дополнение к другим ответам, рассмотрим следующий код:

SomeClass& func() { ... } 

func().memberFunctionOfSomeClass(value); 

Это совершенно естественная вещь и я был бы очень удивлен, если бы вы ожидали, что компилятор даст вам ошибку.

Теперь, когда вы пишете some_obj = value;, что действительно происходит за кулисами, вы звоните some_obj.operator =(value);. И operator =() - это еще одна функция-член вашего класса, отличная от memberFunctionOfSomeClass().

В целом, она сводится к:

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

Конечно, это упрощен, и это обозначение не относится к встроенным типам, как int (но используются одни и те же механизмы).

Надеюсь, это поможет вам лучше понять то, что другие уже объяснили в терминах lvalue.

1

Я тоже был отгорожен аналогичным кодом - в кулаке. «Почему, черт возьми, я назначаю значение вызову функции, и почему компилятор ему доволен?» Я расспросил себя. Но когда вы смотрите на то, что происходит «позади», это имеет смысл.


Как cpp и другая poined вне, lvalues ​​являются «ячейками памяти», которые имеют адрес, и мы можем присвоить им значение. Вы можете найти больше на тему lvalues ​​и rvalues ​​on the internet.

Когда мы посмотрим на функцию:

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

Я переехал & к типу, так что это более очевидно, что мы возвращаемся ссылка на междунар.
Мы видим, что у нас есть x, который является lvalue - он имеет адрес, и мы можем назначить ему. Он также является статичным, что делает его особенным - если он не был статичным, время жизни (область действия) переменной заканчивалось отказом стека после выхода из функции, а затем ссылка могла указывать на то, какая черная дыра существует во вселенной. Однако, поскольку x является статическим, он будет существовать даже после того, как мы оставим функцию (и когда мы снова вернемся к функции), и мы можем получить к ней доступ за пределами функции.

Мы возвращаемся ссылку на междунар, и так как мы вернуть х, это ссылка на х. Затем мы можем использовать ссылку для изменения функции x вне функции. Таким образом:

int main() 
{ 
    fun(); 

Мы просто вызываем функцию. Переменная x (в рамках функции fun) создана, она имеет значение 10 назначенных. Его адрес и значение существуют даже после того, как функция оставлена, но мы не можем использовать ее, поскольку у нас ее нет.

fun() = 30; 

Мы называем функцию, а затем изменить значение х. Значение x изменяется через ссылку, возвращаемую функцией. ПРИМЕЧАНИЕ: эта функция называется первой и только после того, как вызов функции был завершен, тогда происходит назначение.

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

Теперь мы (наконец) сохранить ссылку на х возвращенной функции. Теперь мы можем изменить x без вызова функции в первую очередь.(reference_to_x будет, вероятно, имеет один и тот же адрес, как х есть внутри функции забавной)

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

На этот раз мы создаем новый ИНТ, и мы просто скопировать значение х (через ссылку). Этот новый int имеет свой собственный адрес, он не указывает на x, как reference_to_x есть.

reference_to_x = 5; 

Мы присвоили х значение 5 через ссылку, и мы даже не будем называть функцией. Код copy_of_x не изменяется.

copy_of_x = 15; 

Мы изменили новый Int дорожить 15. х не изменяется, так как copy_of_x иметь свой собственный адрес.

} 


Как 6502 и другие отметили, мы используем подобный подход с возвращением ссылается на много с контейнерами и пользовательских переопределениях.

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

Функция карта, которую мы могли бы использовать заявление, как это:

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

Мы не имеем адрес в строку мы «хранится» в карте, поэтому мы называем эту перекрытый функцию отображения , передавая его, так что карта знает, к какой строке мы хотели бы получить доступ, и возвращает нам ссылку на эту строку, которую мы можем использовать для изменения значения. ПРИМЕЧАНИЕ: снова функция вызывается в первую очередь и только после ее завершения (карта найдена правильная строка и возвращает ссылку на нее) назначение происходит. Это как с удовольствием() = 10, только более красивыми ...

Надеется, что это помогает всем, кто до сих пор woudn't все понять даже после прочтения других ответов ...

Смежные вопросы