Почему функция не вызывает lvalue - PullRequest
1 голос
/ 07 апреля 2019

Это должно быть довольно очевидно, но я не мог бы ни в одной нормативной справке явно указать, что вызов функции (не) является lvalue. Есть несколько связанных вопрос , но он о C ++ и никаких ссылок не предоставляется.

Просматривая 6.5.2.2(p5) Function calls единственное, что я смог найти, это

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

6.3.2.1(p1) утверждает, что

lvalue - это выражение (с другим типом объекта thanvoid), который потенциально обозначает объект

Итак, я попытался определить, обозначает ли вызов функции объект. В Стандарте не указывается, если результат вызова функции имеет длительность и время хранения. Поскольку любой объект имеет длительность и время хранения, я пришел к выводу, что любое выражение вызова функции не обозначает объект и, следовательно, не является lvalue.

Но это кажется запутанным и сложным. В частности я нашел пример 6.5.2.3(p7):

ПРИМЕР 1 Если f - это функция, возвращающая структуру или объединение, а x - это член этой структуры или объединения, f().x is действительный постфикс выражение, но не является lvalue.

Судя по этому примеру, если f() будет lvalue, f().x также будет lvalue. Но примеры информативны, и это меня смутило.

Ответы [ 2 ]

2 голосов
/ 08 апреля 2019

Возвращаемое значение функции не является lvalue, поскольку стандарт определяет термин, но существуют контексты, в которых он может предложить семантику единицы.

Для любого типа структуры:

struct foo {...whatever... };

можно написать функцию, возвращаемое значение которой можно использовать способами, для которых требуется lvalue типа struct foo [чаще всего передача адреса такого lvalue другой функции].

struct wrapped_foo {struct foo it[1];} wrap_foo(foo it)
{
  struct wrapped_foo ret = {it};
  return ret;
}
extern void do_something(int,int,int,struct foo const *p,int,int,int);
void demo_of_passing_address_of_a_foo(struct foo x)
{
  do_something(1,2,3,&(wrap_foo(x).it[0]),4,5,6);
}

Обратите внимание, что хотя возвращаемое значение wrap_foo(x) не является lvalue, wrap_foo(x).it[0] равно единице, и его адрес может быть взят.Время жизни объекта, идентифицированного таким образом, продлится за счет оценки включающего выражения, то есть вызова do_something.Если подписывающий оператор был определен сам по себе как оператор, который не приводит к разложению массива на указатель, а просто возвращает значение типа элемента, которое будет иметь значение l, только если массив равен единице, тогда wrap_foo(x).it[0]не будет lvalue, и проблемы времени жизни не будут иметь значения.

Хотя возможность передавать адрес временного объекта полезна, она добавляет сложности компилятору, требуя, чтобы компилятор предоставил что-то вроде вышеупомянутого пространства для выделенияВозвращаемое значение wrap_foo перед суммированием любых аргументов для вызова внешней функции.Если такая сложность компилятора требуется, он также может позволить такую ​​семантику достигнуть, позволяя выражениям аргумента верхнего уровня использовать & для значений произвольного типа (получая указатель с константной квалификацией для объекта, время жизни которого будетвнешнего включающего выражения).

1 голос
/ 07 апреля 2019

Это не lvalue, потому что в абзаце, который вы цитировали, он описывается как «значение» Стандарт явно упоминает, когда выражение имеет свойство быть lvalue. Например:

6.5.3.2 Операторы адреса и перенаправления (выделено мое)

4 Унарный оператор * обозначает косвенность. Если операнд указывает на функция, результат - обозначение функции; , если он указывает на object, результатом является lvalue, обозначающее объект . Если операнд имеет тип '' указатель на тип '', результат имеет тип '' тип ''. Если указателю присвоено недопустимое значение, поведение унарный * оператор не определен.

Что касается доступа к профсоюзу или члену. Стандарт не требует, чтобы выражение постфикса в expr.id было lvalue. Напротив. Полный доступ к элементу имеет ту же категорию значений, что и выражение постфикса:

6.5.2.3 Структура и члены профсоюза (выделено мое)

3 Постфиксное выражение, за которым следуют оператор . и идентификатор обозначает член структуры или объекта объединения. Значение именованного члена и является lvalue, если первое выражение lvalue . Если первое выражение имеет квалифицированный тип, результат имеет уточненную версию типа назначенного члена.

Итак, в приведенном вами примере f().x - это значение, а не lvalue, потому что f() само по себе не является lvalue.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...