Использовать после перемещения в вызове функции

Когда именно объект, приведенный с помощью std::move, будет перемещен? Например, является ли следующий код использованием после перемещения?

f(std::move(a), a.Something())

Где f = f(A a, int x) и a — экземпляр некоторого класса.

Я понимаю, что std::move(a) может быть оценено раньше a.Something(), но когда происходит сам ход?

std::move на самом деле ничего не перемещает, просто приводит к ссылке rvalue, которая может вызвать вызов соответствующей перегрузки f. Так что это зависит от f.
wohlstad 18.07.2024 18:27

Это зависит от f подписи

Jarod42 18.07.2024 18:27

Если f(A, B), возможно, вам пригодится после переезда, если f(A&&, B), все в порядке.

Jarod42 18.07.2024 18:29

@Jarod42 Ого, спасибо, это только вызывает больше вопросов! (но это отвечает на этот вопрос!)

c z 18.07.2024 18:31

@wohlstad a.Something() оценивается до ввода f(), да. Но порядок оценки параметров не фиксирован, и если первый параметр A передается по значению, то конструкция объекта A может переместить данные a до оценки a.Something().

Remy Lebeau 18.07.2024 18:32

@RemyLebeau, спасибо, теперь я это вижу. Таким образом, перемещение может произойти после завершения копирования в параметр A a.

wohlstad 18.07.2024 18:34

@wohlstad да, именно так

Remy Lebeau 18.07.2024 18:34

Ваш f похож на f(A&& a) и владеет ли он a? (например, отойти от него). Важно, чтобы вы показали полностью минимальный воспроизводимый пример проблемы, о которой у вас есть вопрос.

Pepijn Kramer 18.07.2024 18:40

Кстати, только сегодня мне нужно было что-то вроде *(t++)=func(*t). Поскольку я не был уверен, ставить ли ++ слева или справа, и существует ли вообще правильное решение, я решил, что независимо от правильного решения, если я задаю этот вопрос, другие люди, читающие эту строку, будут слишком. Итак, я решил просто написать это как *t=func(*t); t++;, чтобы всем было понятно. Когда оптимизация включена, компилятор в любом случае скомпилирует эту и правильную «более короткую» версию в один и тот же код. Другими словами: не беспокойтесь и разделите вещи, чтобы их было легче читать.

Christian Stieber 18.07.2024 19:20

Мне кажется, что код UB - нет гарантии, в каком порядке будут оцениваться параметры f

catnip 18.07.2024 23:28

@catnip: UB имеет особое значение (все может случиться). Здесь «просто» порядок вычислений, который не предсказуем (что может привести к неожиданному результату), никакого UB как такового.

Jarod42 19.07.2024 15:43

@ChristianStieber: Даже без оптимизации оба сгенерированных кода будут идентичны.

Jarod42 19.07.2024 15:45

@Jarod42 Jarod42 Итак, «реализация определена»? Это ужасная практика, как бы вы ее ни называли.

catnip 19.07.2024 16:00

Ни одна реализация не определена ;-) (затем компилятор должен указать поведение). Но мы согласны, что такого кода следует избегать :-)

Jarod42 19.07.2024 16:11

@Jarod42 «Не указано», я знал, что в конце концов запомню

catnip 23.07.2024 00:29
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
15
121
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Эта строка:

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 19.07.2024 22:17

@j6t Фактически, сам стандарт не говорит, что оценка аргумента не является последовательностью, он говорит, что «конструкция параметра» не является последовательной, но это включает в себя вычисление выражения, которое вы пишете в позиции соответствующего аргумента expr .call#7: «Инициализация параметра, включая каждое вычисление связанного значения и побочный эффект, имеет неопределенную последовательность по отношению к любому другому параметру».

ABu 20.07.2024 14:41

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