Почему структурированные привязки отключают как 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) (хотя в последнем случае мы сохраняем ссылку). И нет никакого неявного перехода от любого из этих выражений.

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