Когда именно объект, приведенный с помощью std::move
, будет перемещен?
Например, является ли следующий код использованием после перемещения?
f(std::move(a), a.Something())
Где f
= f(A a, int x)
и a
— экземпляр некоторого класса.
Я понимаю, что std::move(a)
может быть оценено раньше a.Something()
, но когда происходит сам ход?
Это зависит от f
подписи
Если f(A, B)
, возможно, вам пригодится после переезда, если f(A&&, B)
, все в порядке.
@Jarod42 Ого, спасибо, это только вызывает больше вопросов! (но это отвечает на этот вопрос!)
@wohlstad a.Something()
оценивается до ввода f()
, да. Но порядок оценки параметров не фиксирован, и если первый параметр A
передается по значению, то конструкция объекта A
может переместить данные a
до оценки a.Something()
.
@RemyLebeau, спасибо, теперь я это вижу. Таким образом, перемещение может произойти после завершения копирования в параметр A a
.
@wohlstad да, именно так
Ваш f
похож на f(A&& a)
и владеет ли он a? (например, отойти от него). Важно, чтобы вы показали полностью минимальный воспроизводимый пример проблемы, о которой у вас есть вопрос.
Кстати, только сегодня мне нужно было что-то вроде *(t++)=func(*t)
. Поскольку я не был уверен, ставить ли ++
слева или справа, и существует ли вообще правильное решение, я решил, что независимо от правильного решения, если я задаю этот вопрос, другие люди, читающие эту строку, будут слишком. Итак, я решил просто написать это как *t=func(*t); t++;
, чтобы всем было понятно. Когда оптимизация включена, компилятор в любом случае скомпилирует эту и правильную «более короткую» версию в один и тот же код. Другими словами: не беспокойтесь и разделите вещи, чтобы их было легче читать.
Мне кажется, что код UB - нет гарантии, в каком порядке будут оцениваться параметры f
@catnip: UB имеет особое значение (все может случиться). Здесь «просто» порядок вычислений, который не предсказуем (что может привести к неожиданному результату), никакого UB как такового.
@ChristianStieber: Даже без оптимизации оба сгенерированных кода будут идентичны.
@Jarod42 Jarod42 Итак, «реализация определена»? Это ужасная практика, как бы вы ее ни называли.
Ни одна реализация не определена ;-) (затем компилятор должен указать поведение). Но мы согласны, что такого кода следует избегать :-)
@Jarod42 «Не указано», я знал, что в конце концов запомню
Эта строка:
f(std::move(a), a.Something());
Действительно может представлять собой использование после переезда.
Если ваш f
:
f(A a, int x) { ... }
Тогда первый аргумент придется либо скопировать, либо переместить в параметр A a
перед вызовом f
. Это означает, что фактический ход также может произойти до того, как будет оценен a.Something()
, что приведет к использованию после хода.
Ничего не стоит, если бы ваш f
был, например:
f(A && a, int x) { ... }
Тогда вы будете в безопасности, потому что std::move
само по себе является просто приведением к rvalue ref, что и ожидается для A && a
, и поэтому на этапе оценки аргументов для f
на самом деле не будет никакого движения.
Когда именно объект, приведенный с помощью std::moved, перемещается?
Это зависит от того, как используется ссылка, возвращаемая std::move()
. std::move()
сам по себе ничего не делает, это просто приведение типов, не более того. Фактическое перемещение происходит, когда что-то еще получает эту ссылку и решает переместить данные объекта, на который ссылается.
Например, является ли следующий код использованием после перемещения?
f(std::move(a), a.Something())
Где
f
=f(A a, int x)
иa
— экземпляр некоторого класса.
МОЖЕТ, но порядок оценки параметров функции не гарантирован. std::move(a)
МОЖЕТ быть оценено в первую очередь или a.Something()
МОЖЕТ быть оценено в первую очередь. Гарантируется только то, что оба параметра будут полностью оценены до фактического ввода f()
.
Я понимаю, что
std::move(a)
может быть оценено раньшеa.Something()
, но когда происходит сам ход?
Поскольку параметр A a
передается в f()
по значению, для него необходимо создать временный объект. Перемещение МОЖЕТ произойти, если возвращаемое значение std::move()
используется для перемещения и построения этого объекта, но только если конструктор A(A&&)
реализован (либо неявно компилятором, либо явно в коде A
), и он действительно выполняет перемещение. Без этого конструктора объект будет создан путем копирования, и, следовательно, перемещения не произойдет.
Стоит отметить, что инициализация параметра является частью оценки аргумента и, следовательно, покрывается утверждением «порядок оценки аргументов не указан».
@j6t Фактически, сам стандарт не говорит, что оценка аргумента не является последовательностью, он говорит, что «конструкция параметра» не является последовательной, но это включает в себя вычисление выражения, которое вы пишете в позиции соответствующего аргумента expr .call#7: «Инициализация параметра, включая каждое вычисление связанного значения и побочный эффект, имеет неопределенную последовательность по отношению к любому другому параметру».
std::move
на самом деле ничего не перемещает, просто приводит к ссылке rvalue, которая может вызвать вызов соответствующей перегрузкиf
. Так что это зависит отf
.