Можно ли использовать NRVO и семантику перемещения вместе в C++?

Я читаю о семантике перемещения в C++ из книги «Семантика перемещения C++». В книге предполагается, что NRVO используется в операторе возврата функции createAndInsert(). Однако там также указано, что семантика перемещения применяется, когда v = createAndInsert(); выполняется в main().

Мой вопрос: если NRVO уже применено, не следует ли coll создавать непосредственно в пространстве для v, что делает ненужными любые операции перемещения в main()? Может ли это быть в учебных целях в книге, или это предполагает, что произойдет, если NRVO не будет применен, несмотря на то, что это заявлено? Я пытаюсь понять, не упускаю ли я что-то о том, как взаимодействуют NRVO и семантика перемещения в этом сценарии.

Спасибо за ваше понимание!

#include <string>
#include <vector>

std::vector<std::string> createAndInsert()
{
    std::vector<std::string> coll;  // create vector of strings
    coll.reserve(3);                // reserve memory for 3 elements
    std::string s = "data";         // create string object

    coll.push_back(s);              // insert string object
    coll.push_back(s+s);            // insert temporary string
    coll.push_back(std::move(s));   // insert string (we no longer need the value of s)

    return coll;                    // return vector of strings
}

int main()
{
    std::vector<std::string> v;     // create empty vector of strings
    //...
    v = createAndInsert();          // assign returned vector of strings
    //...
}

NRVO не является обязательным. Также смотрите Правило неявного хода

user12002570 21.04.2024 15:36

По сути, до тех пор, пока он имеет право на перемещение и может быть неявно перемещаемым объектом и, следовательно, значением x. Смотрите ответ .

user12002570 21.04.2024 15:39

Надеюсь, здесь используется NRVO... Я написал много кода, который предполагает, что это так. Я люблю C++, но меня всегда беспокоит то, что на самом деле делает компилятор. Когда я программировал на C, у меня не было таких забот. На самом деле я бы никогда не вернул объект большого значения в C, хотя я думаю, что лишняя копия исключена.

wcochran 21.04.2024 16: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
3
92
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Если бы вы сделали std::vector<std::string> v = createAndInsert();, сначала был бы переход от coll к возвращаемому временному объекту (NRVO может исключить его, а может и не исключить), затем еще один переход от этого временного значения к v (это всегда опускается, начиная с C++17).

Но при выполнении std::vector<std::string> v; v = createAndInsert(); второй ход пропустить невозможно, поскольку объект уже построен.

Спасибо за ответ! Итак, здесь с std::vector<std::string> v = createAndInsert(); компилятор либо выполняет NRVO (оптимизацию именованного возвращаемого значения), либо применяет семантику перемещения. Если применяется NRVO, дополнительный переход от временного значения к v будет исключен, эффективно преобразуя «coll» непосредственно в «v». Если вместо этого используется семантика перемещения, то операция перемещения переносит временное возвращаемое значение в v. Я прав?

Sami 22.04.2024 14:57

@Сами Не совсем. Перемещение временного значения в v всегда опускается. Предыдущее перемещение coll во временную область может быть исключено, а может и не быть (исключено, если компилятор выполняет NRVO).

HolyBlackCat 22.04.2024 14:59

С NRVO (оптимизация именованного возвращаемого значения): Если применяется NRVO: локальный вызов createAndInsert() создается непосредственно там, где будет v, исключая любые операции перемещения. Если NRVO не применяется: Col перемещается во временный объект, содержащий возвращаемое значение. Без NRVO: Первое перемещение: coll перемещается во временный возвращаемый объект. Второй ход: этот переход (от временного к v) исключен в C++17 из-за обязательного исключения копирования, что означает, что временное создается непосредственно как v. AM, я правильно?

Sami 22.04.2024 15:04

@Сами Да, звучит правильно.

HolyBlackCat 22.04.2024 15:05

С NRVO (Именованная оптимизация возвращаемого значения):

Если применяется NRVO: Локальный объект coll внутри функции createAndInsert() создается непосредственно в той области памяти, которую v займет после завершения функции. Это избавляет от необходимости перемещать объект, поскольку Col по сути создан как v.

Если NRVO не применяется: Не существует прямой конструкции Col в v. Вместо этого Col необходимо переместить во временный объект, который представляет возвращаемое значение createAndInsert().

Без NRVO:

Переход от колла к возвращенному временному:

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

Переход от временного к v:

До C++17 это была вторая операция перемещения, при которой временное перемещается для инициализации v. Однако, начиная с C++17, это перемещение исключается обязательными правилами исключения копирования, эффективно создавая временное пространство непосредственно в выделенном пространстве. для В.

Что происходит в каждом случае:

Инициализация (std::vector<std::string> v = createAndInsert();): Если применяется NRVO, coll переходит непосредственно к v без каких-либо ходов. Если NRVO не применяется, coll перемещается во временный объект, а затем этот временный объект создается на месте как v (второе перемещение исключено, начиная с C++17).

Задание (std::vector<std::string> v; v = createAndInsert();): Независимо от NRVO, поскольку v уже создан, вектор из createAndInsert() (будь то кол или временный) перемещается в v с использованием назначения перемещения, и это перемещение нельзя исключить.

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