Я читаю о семантике перемещения в 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
//...
}
По сути, до тех пор, пока он имеет право на перемещение и может быть неявно перемещаемым объектом и, следовательно, значением x. Смотрите ответ .
Надеюсь, здесь используется NRVO... Я написал много кода, который предполагает, что это так. Я люблю C++, но меня всегда беспокоит то, что на самом деле делает компилятор. Когда я программировал на C, у меня не было таких забот. На самом деле я бы никогда не вернул объект большого значения в C, хотя я думаю, что лишняя копия исключена.
Если бы вы сделали 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. Я прав?
@Сами Не совсем. Перемещение временного значения в v
всегда опускается. Предыдущее перемещение coll
во временную область может быть исключено, а может и не быть (исключено, если компилятор выполняет NRVO).
С NRVO (оптимизация именованного возвращаемого значения): Если применяется NRVO: локальный вызов createAndInsert() создается непосредственно там, где будет v, исключая любые операции перемещения. Если NRVO не применяется: Col перемещается во временный объект, содержащий возвращаемое значение. Без NRVO: Первое перемещение: coll перемещается во временный возвращаемый объект. Второй ход: этот переход (от временного к v) исключен в C++17 из-за обязательного исключения копирования, что означает, что временное создается непосредственно как v. AM, я правильно?
@Сами Да, звучит правильно.
С 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 с использованием назначения перемещения, и это перемещение нельзя исключить.
NRVO не является обязательным. Также смотрите Правило неявного хода