Возвращаемое значение функции не является 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
перед суммированием любых аргументов для вызова внешней функции.Если такая сложность компилятора требуется, он также может позволить такую семантику достигнуть, позволяя выражениям аргумента верхнего уровня использовать &
для значений произвольного типа (получая указатель с константной квалификацией для объекта, время жизни которого будетвнешнего включающего выражения).