Неявно обрабатывать возвращаемое значение 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;