Эта программа:
#include <iostream>
struct T {
T() {}
T(const T &) { std::cout << "copy constructor "; }
T(T &&) { std::cout << "move constructor "; }
};
int main() {
([](T t) -> T { return t; })({}); std::cout << '\n';
([](T t) -> T { return void(), t; })({}); std::cout << '\n';
([](T t) -> T { return void(), std::move(t); })({}); std::cout << '\n';
}
при компиляции выходами gcc-4.7.1 (ссылка на сайт):
move constructor
copy constructor
move constructor
Почему оператор запятой имеет такой эффект? Стандарт гласит:
5.18 Оператор запятой [expr.comma]
1 — […] тип
и значением результата являются тип и значение правого операнда; результат имеет ту же категорию значений, что и его правый операнд […]. Если значение правого операнда является временным, то результат является временным.
Я пропустил что-то, что позволяет оператору запятой влиять на семантику программы, или это ошибка в gcc?
Автоматическое перемещение основано на праве на получение копии:
§12.8 [class.copy] p32
Когда критерии для исключения операции копирования выполнены или будут выполнены, за исключением того факта, что исходный объект является параметром функции, а копируемый объект обозначен lvalue, разрешением перегрузки для выбора конструктора для копирования является сначала выполняется так, как если бы объект был обозначен как rvalue. […]
И копия elision, в свою очередь, разрешена, когда возвращаемое выражение является название автоматического объекта.
§12.8 [class.copy] p31
в
return
оператор в функции с типом возвращаемого класса, когда выражение является именем энергонезависимого автоматического объекта (кроме параметра функции или оператора catch) с тем же типом cv-unqualified, что и тип возвращаемого функцией, операцию копирования / перемещения можно опустить, создав автоматический объект непосредственно в возвращаемое значение функции
При вставленном операторе запятой выражение больше не является именем автоматического объекта, а только ссылкой на объект, который подавляет удаление копии.
t
является локальная именованная переменная и, следовательно, lvalue. Оператор запятой ведет себя как задокументировано.
Скорее, вы должны спросить, почему return t;
позволяет t
привязать к rvalue ссылке — тотнастоящая магия