Почему структурированные привязки отключают как RVO, так и оператор return?
Предположим, что у нас есть класс с именем AAA
который поддерживает как copy/move:
class AAA
{
public:
AAA() = default;
~AAA() = default;
AAA(const AAA& rhs)
{
std::cout << "Copy constructor" << std::endl;
}
AAA(AAA&& rhs)
{
std::cout << "Move constructor" << std::endl;
}
};
В следующем коде get_val
возвращает second
:
AAA get_val()
{
auto [ first, second ] = std::make_tuple(AAA{}, AAA{});
std::cout << "Returning - " << std::endl;
return second;
}
auto obj = get_val();
std::cout << "Returned - " << std::endl;
Теперь second
копируется, печатая следующий вывод:
...
Returning -
Copy constructor
Returned -
Это печально, потому что мое ожидание результата - либо нет вызова конструктора копирования, либо, по крайней мере, он неявно перемещается.
Чтобы избежать копирования, я должен будет явно применить std::move
на нем.
return std::move(second);
Тогда я получаю результат:
...
Returning -
Move constructor
Returned -
Я предполагаю, что причина, по которой RVO не выполняется, состоит в том, что, вероятно, компиляторы будут видеть second
как ссылку, а get_val
возвращает значение prvalue.
Однако почему нельзя ожидать неявного движения? Использование явного std::move
в операторе return не выглядит интуитивно понятным в данном конкретном случае, потому что вы вообще не хотите делать RVO, что в большинстве случаев лучше, чем движение, случайно удалилось.
Протестировано обоими компиляторами gcc и clang с -O3
.
Демо-версия
Ответы
Ответ 1
Однако почему нельзя ожидать неявного движения?
По той же причине, что и элиция, отключена: потому что это ссылка, а не имя независимого объекта. Каждое использование second
по существу эквивалентно obj.whatever
или get<1>(obj)
(хотя в последнем случае мы сохраняем ссылку). И нет никакого неявного перехода от любого из этих выражений.
Структурированное связывание предназначено для доступа к подобъектам данного объекта. Вы не можете исключить возврат подобъектов, и вы не можете имплицитно перейти от них. Таким образом, вы не можете элиминировать структурированные имена привязок и неявно перемещаться от них.