RVO, когда с возвращаемым значением происходит неявное преобразование

Рассмотрим следующий пример:

#include <optional>
#include <string>

struct S {
    std::string text = "hello";
};

std::optional<S> foo() {
    S s;
    return s;
}

std::optional<S> bar() {
    S s;
    return std::move(s);
}

Я правильно понимаю, что foo и bar идентичны, поскольку компилятор выполняет RVO и автоматически перемещает s при передаче его в std::optional<S>?

Значит, общее правило «Не перемещайте то, что возвращаете» применяется и тогда, когда произойдет неявное преобразование?

RVO означает что-то другое, но да, std::move здесь не нужен.

HolyBlackCat 26.04.2024 13:43
std::optional делает RVO немного сложнее. Вот Джейсон Тернер объясняет проблему.
Marek R 26.04.2024 14:09

Для чего-то столь тривиального компилятор будет просматривать все насквозь. И он оптимизирует почти все. std::move in bar — это пессимистичный шаг, и вам не следует его делать, он может даже нарушить оптимизацию.

Pepijn Kramer 26.04.2024 14:11

См. этот ответ, в котором объясняется, что операндом оператора return (если он удовлетворяет некоторым условиям) является rvalue(xvalue), а также упомянутое там правило неявного перемещения.

user12002570 26.04.2024 15:39
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
83
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

То, что вы здесь описываете, - это не то, что люди обычно подразумевают под «RVO» («оптимизация возвращаемого значения»), хотя этот термин сам по себе является разговорным и не встречается в стандарте.

RVO обычно относится к ситуации до C++17, когда возвращаемое значение prvalue не копируется:

T f();

T g() { return f(); }

В этом примере RVO означает, что при вычислении g() нет второй копии, и вместо этого результат f() создается непосредственно в возвращаемом значении g.

С принятием P0135 для C++17 (несколько неправильно названного, но широко называемого «гарантированным исключением копирования») такое поведение теперь является обязательным, начиная с C++17, поэтому на самом деле не существует «RVO». " больше не называется "оптимизацией" — именно так теперь работает C++. Ключевое концептуальное изменение заключается в том, что prvalues ​​больше не рассматриваются как «материализованные значения» (с хранилищем), а скорее как «инструкции по инициализации объекта».

Однако ничто из этого не имеет отношения к вашему примеру, части которого в просторечии известны как «оптимизация именованного возвращаемого значения» («NRVO»), но это только половина дела. Другая половина заключается в том, что если оптимизация (которая будет состоять из создания значения непосредственно в возвращаемом значении, а не «в стеке») фактически не выполняется, то локальная переменная считается rvalue, когда она появляется как операнд заявление о возврате.

Первоначально в первой версии C++11 такое поведение работало только тогда, когда тип переменной точно соответствовал типу возвращаемого значения функции, но CWG 1579 задним числом изменил это поведение, так что теперь, начиная с C++11, всегда было так, что возвращаемая переменная считается rvalue, независимо от ее типа (и std::optional было основной мотивацией для этого исправления). Например, теперь это работает:

std::optional<std::unique_ptr<int>> f() {
  auto p = std::make_unique<int>(1);
  return p;
}

Со времени C++17 произошли дополнительные изменения, благодаря которым больше вещей «считаются rvalue», когда они появляются как выражение в операторе return или выражении throw, поэтому обычно существует тенденция «пытаться переместить вещи, когда они могут». явно больше не будет использоваться впоследствии"; см. P2266R3, принятый для C++23.

Другие вопросы по теме