Подтвердить что ты не робот

Является ли членом структуры rval значение rvalue или lvalue?

Вызов функции, возвращающий структуру, является выражением rvalue, но как его члены?
Этот фрагмент кода хорошо работает с моим компилятором g++, но gcc дает сообщение об ошибке "lvalue, требуемое как левый операнд назначения":

struct A
{
    int v;
};

struct A fun()
{
    struct A tmp;
    return tmp;
}

int main()
{
    fun().v = 1;
}

gcc рассматривает fun().v как rvalue, и я могу это понять.
Но g++ не считает, что выражение присваивания неверно. Означает ли это fun1(). V является lvalue в С++?
Теперь проблема в том, что я искал стандарт С++ 98/03, ничего не узнавая о том, является ли fun().v значением lvalue или rvalue.
Итак, что это?

4b9b3361

Ответ 1

Член выражения rvalue является rvalue.

Стандартные состояния в 5.3.5 [expr.ref]:

Если объявлен тип E2 типа "ссылка на T", то E1.E2 является lvalue [...] - Если E2 - нестатический элемент данных, а тип E1 - "cq1 vq1 X" и тип E2 является "cq2 vq2 T", выражение обозначает именованный элемент объекта, обозначенного первым выражение. Если E1 - значение l, то E1.E2 является lvalue.

Ответ 2

Изменить: Хорошо, я догадываюсь, что у меня есть что-то из стандарта:

Обратите внимание, что v имеет тип int, который имеет встроенный оператор присваивания:

13.3.1.2 Операторы в выражениях

4 Для встроенных операторов присваивания преобразования левого операнда ограничены следующим образом: - не вводятся временные значения для удерживания левого операнда и [...]

fun1() должен возвращать ссылку. Тип ссылки, не ссылающийся/указатель, является значением r.

3.10 Lvalues ​​и rvalues ​​

5 Результатом вызова функции, которая не возвращает ссылку lvalue, является rvalue [...]

Таким образом, fun1().v является значением r.

8.3.2 Ссылки

2 Тип ссылки, который объявлен используя и называется ссылкой lvalue, и ссылочный тип, который объявлен используя && называется rvalue Справка. Ссылки на Lvalue и Ссылки rvalue - это разные типы.

Ответ 3

Я заметил, что gcc имеет очень мало компромиссов относительно использования rvalues ​​как lvalues ​​в выражениях присваивания. Это, например, компилируется просто отлично:

class A {
};

extern A f();

void g()
{
   A myA;
   f() = myA;
}

Почему это юридическое, а это нет (т.е. он не компилируется), хотя меня действительно смущает:

extern int f();

void g()
{
   f() = 5;
}

ИМХО, у стандартного комитета есть некоторые объяснения относительно lvalues, rvalues ​​и где они могут быть использованы. Это одна из причин, почему меня так интересует этот вопрос о rvalues ​​.

Ответ 4

Это становится очевидным, если вы считаете, что компилятор будет генерировать конструктор по умолчанию, конструктор копии по умолчанию и стандартный оператор присваивания копии для вас, если ваш struct/class не содержит ссылочные элементы. Затем подумайте, что стандарт позволяет вам вызывать методы-члены во временных рядах, то есть вы можете вызывать неконстантные члены в неконстантных временных рядах.

См. этот пример:

struct Foo {};
Foo foo () {
    return Foo();
}

struct Bar {
private:
    Bar& operator = (Bar const &); // forbid
};
Bar bar () {
    return Bar();
}
int main () {
    foo() = Foo(); // okay, called operator=() on non-const temporarie
    bar() = Bar(); // error, Bar::operator= is private
}

Если вы пишете

struct Foo {};
const Foo foo () { // return a const value
    return Foo();
}

int main () {
    foo() = Foo(); // error
}

то есть. если вы разрешите функции foo() возвращать a const, то возникает ошибка компиляции.

Чтобы сделать пример полным, вот как вызвать член const temporarie:

struct Foo {
    int bar () const { return 0xFEED; }
    int frob ()      { return 0xFEED; }
};
const Foo foo () {
    return Foo();
}

int main () {
    foo().bar(); // okay, called const member method
    foo().frob(); // error, called non-const member of const temporary
}

Вы можете определить время жизни временного объекта в текущем выражении. И тогда вы также можете изменить переменные-члены; если бы вы не могли, то вероятность того, что вы сможете вызвать методы, не являющиеся константами, будет вестись ad absurdum.

edit: И вот требуемые цитаты:

12.2 Временные объекты:

  • 3) [...] Временные объекты уничтожаются как последний шаг при оценке полного выражения (1.9), который (лексически) содержит точку, в которой они были созданы. [...]

а затем (или лучше, раньше)

3.10 Lvalues ​​и rvalues:

  • 10) Для изменения объекта требуется lvalue для объекта, за исключением того, что rvalue типа класса также может использоваться для изменения его референта при определенных обстоятельствах. [Пример: функция-член, вызываемая для объекта (9.3), может изменять объект. ]

И пример использования: http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Named_Parameter

Ответ 5

Это хорошее время, чтобы узнать, что xvalues ​​ glvalues ​​.

Rvalues ​​ может быть двух типов - prvalues ​​ и xvalues ​​. Согласно новому стандарту С++ 17

A prvalue - выражение, чья оценка инициализирует объект, бит-поле или операнд оператора, как определено контекстом, в котором он появляется.

поэтому что-то вроде fun() в вашем примере оценивает значение prvalue (которое является rvalue). Это также говорит о том, что fun().v не является prvalue, так как это не инициализация ванили.

Xvalues ​​, которые также являются значениями r, определены так:

xvalue (значение "eXpiring" ) также относится к объекту, обычно ближе к концу его жизненного цикла (так что его ресурсы могут быть перемещены, например). Определенные типы выражений с использованием ссылок rvalue (8.3.2) дают значения x. [Пример: результат вызова функции, тип возврата которой является ссылкой rvalue на тип объекта, представляет собой значение xvalue (5.2.2). - конец примера]

В дополнение к rvalues ​​другая категория значений зонтика - это glvalue, которая имеет два типа xvalues ​​ и традиционные lvalues ​​.

В этой точке мы определили основные категории значений. Это можно визуализировать так:

введите описание изображения здесь

В категории glvalue можно в целом понимать, что означали lvalues ​​, прежде чем перемещение семантики стало вещью - вещь, которая может быть в левой части выражение. glvalue означает обобщенное значение lvalue.

Если мы посмотрим на определение xvalue, то он говорит, что что-то есть xvalue, если оно близко к концу его жизни. В вашем примере fun().v находится ближе к концу своего срока службы. Таким образом, его ресурсы могут быть перемещены. И поскольку его ресурсы можно перемещать, это не значение lvalue, поэтому ваше выражение подходит только к единственной категории значений листьев - xvalue.

Ответ 6

У вас нет сцены. Возвращенная структура распределяется по стеку, поэтому результат присваивания немедленно теряется.

Ваша функция должна eiter выделять новый экземпляр A по:

new A()

В этом случае лучшая подпись

A* f(){ ...

Или вернуть существующий экземпляр, например:

static A globalInstance;
A& f(){ 
  return globalInstance;
}