Неявно обрабатывать возвращаемое значение lvalue как rvalue

12.8 Копирование и перемещение объектов класса [class.copy] §31 и §32 говорят:

в операторе return в функции с типом возвращаемого класса , когда выражение представляет собой имя нелетучего автоматического объекта (кроме функции или параметра catch-clause) с тем же cv-unqualified type в качестве возвращаемого типа функции, операцию копирования/перемещения можно опустить, построив автоматический объект непосредственно в возвращаемое значение функции

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

Следовательно, мы можем написать:

unique_ptr<int> make_answer()
{
    unique_ptr<int> result(new int(42));
    return result;   // lvalue is implicitly treated as rvalue
}

Однако я заметил, что g++ 4.6.3 также принимает lvalues, которые не имена, например:

    return (result);
    return *&result;
    return true ? result : result;

В отличие от этого, return rand() ? result : result; не работает. Оптимизатор компилятора вмешивается в семантику языка? Поскольку я интерпретирую стандарт, return (result); не должен компилироваться, потому что (result) не является именем, а выражением в скобках. Правильно или неправильно?

Ответ 1

Что касается заключенных в скобки выражений [√]

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

5.1.1/1     Общие     [Expr.prim.general]

Обозначенное в скобках выражение является первичным выражением, тип и значение идентичны значениям прилагаемого выражения. наличие скобок не влияет на то, является ли выражение lvalue. Выражение в скобках может использоваться точно в тех же контекстах, что и те, в которых может использоваться замкнутое выражение, и с тем же значением, если не указано иное.


Что касается условного оператора constexpr [╳]

То, как я интерпретирую стандарт в отношении константных выражений, и его оператор кодирования состоит в том, что использование return true ? result : result хорошо себя ведет, поскольку оно является постоянным выражением и поэтому эквивалентно возврату результата;забастовкa >

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

true ? <expr1> : <expr2>; // this is not the same as just writing <expr1>;

Относительно возврата * & result; [╳]

В C99 явно указано, что *&result является точным эквивалентом написания result, это не так в спецификации С++.

Хотя мы все можем согласиться с тем, что использование *&result действительно приведет к тому же lvalue, что и result, но согласно стандарту *&result (конечно) не является выражением, где "выражение - это имя энергонезависимый автоматический объект".

Конечно, выражение содержит соответствующее имя, но не только это.


Подводя итог...

return result; // #1, OK

return (result);                  // as described earlier, OK
return true ? result : result;    // as described earlier, ill-formed
return rand () ? result : result; // as described earlier, ill-formed
return *&result;                  // as described earlier, ill-formed

Ответ 2

Обозначенные в скобках выражения эквивалентны их несферированным выражениям, если не указано иное (как это сделано в правилах ADL, например, или с помощью decltype для другого примера). Иногда бывает сложно, когда и когда это не эквивалентно такому примеру (например, в правилах ADL явно не упоминаются "unparenthesized", но они используют явную грамматику без терминалов и примеры, которые дают понять, что parens не считаются эквивалентными).

По другим вопросам: Да, GCC делает несколько оптимизаций непосредственно в AST, что позволяет принимать различные недействительные программы, например следующие

int a = 42;
int *p = 0 * a;