Что означает int& foo() в C++?
Когда я читал это объяснение о lvalues и rvalues, мне показались следующие строки кода:
int& foo();
foo() = 42; // OK, foo() is an lvalue
Я пробовал это в g++, но компилятор говорит "неопределенная ссылка на foo()". Если я добавлю
int foo()
{
return 2;
}
int main()
{
int& foo();
foo() = 42;
}
Он компилируется нормально, но при его запуске возникает ошибка сегментации. Просто линия
int& foo();
сам по себе и компилируется и запускается без проблем.
Что означает этот код? Как вы можете назначить значение для вызова функции, и почему это не rvalue?
9 ответов
Объяснение предполагает, что существует некоторая разумная реализация foo
который возвращает ссылку lvalue на действительный int
,
Такая реализация может быть:
int a = 2; //global variable, lives until program termination
int& foo() {
return a;
}
Теперь, так как foo
возвращает ссылку на lvalue, мы можем присвоить возвращаемому значению что-то вроде этого:
foo() = 42;
Это обновит глобальный a
со значением 42
, который мы можем проверить, обращаясь к переменной напрямую или вызывая foo
снова:
int main() {
foo() = 42;
std::cout << a; //prints 42
std::cout << foo(); //also prints 42
}
Все остальные ответы объявляют статическое внутри функции. Я думаю, что это может сбить вас с толку, так что взгляните на это:
int& highest(int & i, int & j)
{
if (i > j)
{
return i;
}
return j;
}
int main()
{
int a{ 3};
int b{ 4 };
highest(a, b) = 11;
return 0;
}
Так как highest()
возвращает ссылку, вы можете присвоить ей значение. Когда это работает, b
будет изменено на 11. Если вы изменили инициализацию так, чтобы a
было, скажем, 8, то a
будет изменено на 11. Это некоторый код, который может фактически служить цели, в отличие от других примеров.
int& foo();
Объявляет функцию с именем foo, которая возвращает ссылку на int
, Что эти примеры не могут сделать, так это дать вам определение функции, которую вы можете скомпилировать. Если мы используем
int & foo()
{
static int bar = 0;
return bar;
}
Теперь у нас есть функция, которая возвращает ссылку на bar
, так как бар static
он будет работать после вызова функции, поэтому возвращение ссылки на него безопасно. Теперь, если мы сделаем
foo() = 42;
Что происходит, мы назначаем 42 bar
так как мы назначаем ссылку, а ссылка просто псевдоним для bar
, Если мы вызовем функцию снова, как
std::cout << foo();
Было бы напечатать 42, так как мы установили bar
к этому выше.
int &foo();
объявляет функцию с именем foo()
с типом возврата int&
, Если вы вызываете эту функцию без предоставления тела, то вы, вероятно, получите неопределенную ошибку ссылки.
Во второй попытке вы предоставили функцию int foo()
, Это имеет тип возврата, отличный от функции, объявленной int& foo();
, Итак, у вас есть две декларации одного и того же foo
это не соответствует, что нарушает Правило Единого Определения, приводящее к неопределенному поведению (диагностика не требуется).
Для чего-то, что работает, возьмите объявление локальной функции. Они могут привести к тихому неопределенному поведению, как вы видели. Вместо этого используйте только объявления функций вне какой-либо функции. Ваша программа может выглядеть так:
int &foo()
{
static int i = 2;
return i;
}
int main()
{
++foo();
std::cout << foo() << '\n';
}
int& foo();
это функция, возвращающая ссылку на int
, Ваша функция возвращает int
без ссылки.
Вы можете сделать
int& foo()
{
static int i = 42;
return i;
}
int main()
{
int& foo();
foo() = 42;
}
int & foo();
Значит это foo()
возвращает ссылку на переменную.
Рассмотрим этот код:
#include <iostream>
int k = 0;
int &foo()
{
return k;
}
int main(int argc,char **argv)
{
k = 4;
foo() = 5;
std::cout << "k=" << k << "\n";
return 0;
}
Этот код печатает:
$./a.out k = 5
Так как foo()
возвращает ссылку на глобальную переменную k
,
В своем исправленном коде вы преобразуете возвращаемое значение в ссылку, которая затем становится недействительной.
В этом контексте & означает ссылку - поэтому foo возвращает ссылку на int, а не на int.
Я не уверен, что вы уже работали с указателями, но это похожая идея, вы на самом деле не возвращаете значение из функции - вместо этого вы передаете информацию, необходимую для поиска места в памяти, где это int is.
Подводя итог, вы не назначаете значение для вызова функции - вы используете функцию для получения ссылки, а затем присваиваете значение, на которое ссылается новое значение. Легко думать, что все происходит одновременно, но на самом деле компьютер делает все в точном порядке.
Если вам интересно - причина, по которой вы получаете segfault, заключается в том, что вы возвращаете числовой литерал '2' - так что это именно та ошибка, которую вы получите, если будете определять const int, а затем пытаться изменить его значение.
Если вы еще не узнали об указателях и динамической памяти, я бы рекомендовал это сначала, поскольку есть несколько концепций, которые, как мне кажется, трудно понять, если вы не изучите их все сразу.
Пример кода на связанной странице - просто фиктивное объявление функции. Он не компилируется, но если бы вы определили какую-то функцию, она бы работала в целом. Пример означал "Если бы у вас была функция с этой подписью, вы могли бы использовать ее вот так".
В вашем примере foo
явно возвращает значение lvalue на основе подписи, но вы возвращаете значение r, которое преобразуется в значение lvalue. Это явно решено потерпеть неудачу. Вы могли бы сделать:
int& foo()
{
static int x;
return x;
}
и преуспеет, изменив значение х, говоря:
foo() = 10;
Ваша функция foo() - это функция, которая возвращает ссылку на целое число.
Допустим, первоначально foo вернул 5, а позже, в вашей основной функции, вы говорите foo() = 10;
, затем распечатывает foo, он напечатает 10 вместо 5.
Надеюсь это имеет смысл:)
Я новичок в программировании. Интересно видеть такие вопросы, которые заставляют вас задуматься!:)