Требуется ли конструктор/назначение перемещения для запуска RVO в С++ 11?

Например:

В принятом ответе https://stackoverflow.com/a/14623480/1423254

Будет ли копирование elision и RVO по-прежнему работать для классов без конструкторов перемещения?

Да, RVO все еще срабатывает. На самом деле ожидается, что компилятор выберет: RVO (если возможно)

В принятом ответе https://stackoverflow.com/a/38043447/1423254

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

Дело в том, что я думал, что перемещение RVO и lvalue (или как их назвать) — это две совершенно разные операции, но мой коллега сказал мне, что для запуска RVO возвращаемый класс нуждается в конструкторе перемещения. Итак, я проверил Интернет и ТАК, и, очевидно, информацию нельзя найти быстро...


person Lukas Salich    schedule 26.06.2019    source источник


Ответы (2)


Нет, если конструктор копирования доступен.

До появления C++17 и гарантированного исключения копирования для того, чтобы код был допустимым, должен был быть доступен либо конструктор перемещения ИЛИ конструктор копирования. Elision была просто оптимизацией и не влияла на то, будет ли код компилироваться или нет.

То есть происходит двухэтапный процесс:

  • Оцените, допустима ли инструкция return <expr>;, которая до C++17 требовала (за исключением преобразований), чтобы либо конструктор копирования, либо, при наличии временного, конструктор перемещения присутствовал и был доступен.
  • Если возможно, примените оптимизацию возвращаемого значения и исключите вызов указанного конструктора.

Имея это в виду, давайте рассмотрим цитаты:

Будет ли копирование elision и RVO по-прежнему работать для классов без конструкторов перемещения?

Да, RVO все еще срабатывает. На самом деле ожидается, что компилятор выберет: RVO (если возможно)

Да следует понимать в контексте допустимого кода. Если код не компилируется, то вопрос спорный.

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

Это короткий путь; попытка сформулировать предложение с учетом возможного отсутствия конструктора перемещения и, следовательно, отката к конструктору копирования просто запутает объяснение, и ОП не дал никаких указаний на то, что рассматривалась такая ситуация.

person Matthieu M.    schedule 26.06.2019

Краткий ответ: нет.

RVO также существовал до C++11, когда не было такого понятия, как "конструктор перемещения". В этом случае единственным требованием был конструктор копирования.

Параграф "Согласно правилам исключения негарантированных копий...", который вы цитируете, относится к миру до C++17, где "обязательное исключение копирования" еще не было частью языка.

Начиная с C++17, следующий код компилируется (с гарантией отсутствия копий/перемещений):

struct foo
{
    foo() = default;
    foo(const foo&) = delete;
    foo(foo&&) = delete;
};

foo get_foo() { return foo{}; }

int main()
{
    foo f{get_foo()};
}
person Vittorio Romeo    schedule 26.06.2019
comment
Так почему же в версии до C++17 есть утверждение: эта операция перемещения может быть опущена, но T все равно должен иметь доступный конструктор перемещения, даже если он никогда не используется? Это означало бы, что без конструктора перемещения RVO не сработает, и в этом суть. - person Lukas Salich; 26.06.2019
comment
@LukasSalich: если конструктор перемещения недоступен, используется копия. Вот как обычно работает операция перемещения. Нет смысла везде втыкать copy/move. - person Nicol Bolas; 26.06.2019