Хорошая ли идея использовать std::move с функцией, которая получает константную ссылку?

Вот фрагмент, который компилируется и работает:

#include <iostream>
#include <string>

void print_str(const std::string& str) {
    std::cout << str << '\n';
}

int main() {
    std::string str = "Hello world";
    print_str(std::move(str));
}

Богболт

Насколько я понимаю, std::move здесь не действует.

Два вопроса:

  • Правильно ли, что std::move здесь не вреден, а просто не имеет эффекта?
  • Будет ли его использование в любом случае считаться хорошей практикой? Если я не контролирую print_str и не нуждаюсь в str после вызова, можно сказать, что std::move будет иметь положительный эффект, если в какой-то момент print_str будет изменено, чтобы получить std::string по значению.

Нет, это не будет хорошей практикой. Вы используете std::move, чтобы указать на передачу права собственности и на то, что вы хотите вызвать print_str(std::string&& str). Передача по ref указывает на то, что передачи права собственности быть не должно.

Pepijn Kramer 28.04.2024 14:07

Да, и да. Вы закончили с str и указываете, передав право собственности, что print_str может использовать его, если это необходимо (и вам нужно явно передать право собственности, в этом случае компилятор не сможет сделать это за вас, несмотря на то, что это «очевидно»). Тот факт, что этого не происходит, является деталью реализации, и это может измениться через день, месяц или год. Благодаря этой семантике C++11 оставался совместимым со всем существующим кодом C++03.

StoryTeller - Unslander Monica 28.04.2024 14:16

В C++ не рекомендуется всегда что-то делать, даже если вы не знаете, почему.

BoP 28.04.2024 14:17

@BoP - Тем более, когда ты не знаешь почему.

Peter 28.04.2024 14:20

1. Это вредно для читателей этого кода :) - когда кто-то читает код, содержащий std::move, он думает, что вы хотите использовать конструктор перемещения. 2. Почему print_str будет изменен, чтобы получить std::string по значению? :) Лучше использовать string_view.

Pavel 28.04.2024 14:36
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
5
160
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Вы правы, move здесь не имеет никакого эффекта, и вопрос о том, использовать ли это, полностью зависит от вашего мнения.

  • С одной стороны, это действительно может улучшить производительность, если/когда print_str изменить таким образом. Он также выражает намерение больше не использовать str.

  • С другой стороны, это может быть момент WTF для будущего читателя вашего кода. Кроме того, если после этого у вас есть код, который (неправильно) считывается из str после бездействия, if сломается, когда print_str обновится таким образом.

Лично я бы сделал это только если:

  • print_str выглядит так, будто он должен принимать аргумент по значению, но в настоящее время этого не происходит (например, установщик, который должен переместить строку в класс, но в настоящее время копирует ее)
  • Я выполняю короткую функцию, в которой трудно случайно повторно использовать строку после перемещения.

Вы говорите, что это основано на мнениях, но не все мнения имеют одинаковую ценность. Вот в чем дело: использование str после std:move по ошибке на самом деле не является проблемой. Это можно пометить как с помощью проверки кода, так и с помощью инструментов. WTF для менее опытных также не является аргументом, поскольку это предотвратило бы 90% эволюции C++. Мы не можем вечно писать код с наименьшим общим знаменателем.

StoryTeller - Unslander Monica 28.04.2024 14:24

@StoryTeller-UnslanderMonica Видимо, мы можем продолжать писать к наименьшему общему знаменателю вечно - я только что боролся с некоторыми VBA в Excel.

Peter 28.04.2024 14:35

Я не согласен с «мнением, основанным здесь». Написание хорошего кода также подразумевает выражение намерений. Если вы не собираетесь передавать право собственности, не следует использовать std::move (даже если это всего лишь приведение типов)

Pepijn Kramer 28.04.2024 14:49

@PepijnKramer Это один из пунктов этого ответа. Вы намерены передать право собственности «если/когда print_str будет изменено [принять std::string&&

Artyer 28.04.2024 15:36

@PepijnKramer std::move не всегда выражает передачу права собственности. В шаблоне это вполне может выражаться: «Я больше не буду использовать этот объект, вы можете делать с ним все, что захотите» (т. е. «передать право собственности, если применимо»). Например. если у вас есть шаблон с std::move, а затем создайте его экземпляр для int, вы наверняка не будете беспокоиться о бесполезном движении. Если нас устраивает значение move в шаблонах, я думаю, что вне шаблонов оно не обязательно плохо.

HolyBlackCat 28.04.2024 16:17

Два человека не согласны с «основанным на мнениях» и поддерживают разные мнения. :П

HolyBlackCat 28.04.2024 16:20

@StoryTeller-UnslanderMonica Фактор WTF меня тоже не остановил бы, но я бы добавил короткий комментарий, объясняющий, почему здесь есть move.

HolyBlackCat 28.04.2024 16:20

@HolyBlackCat - я уверен, что к тому времени, когда этот вопрос станет горячим в сети, не будет N несогласных людей и N + 1 мнений :p

StoryTeller - Unslander Monica 28.04.2024 16:34

Читая комментарии, могу согласиться, что это основано на мнениях :) Кстати, я также обнаружил, что статический анализатор PVS-Studio показывает предупреждение для такого рода std::move (внизу страницы).

Paul 28.04.2024 17:00

@PepijnKramer Передача права собственности для меня не имеет смысла для std::string. Право собственности на что? Буфер, выделенный в куче, которым случайно владеет std::string, если он превышает предел SSO? std::move с определенными типами, такими как string, предназначен для повышения производительности, просто и понятно. Семантическая передача владения имеет смысл для типов, которые концептуально владеют чем-то, например std::unique_ptr. В мире, где производительность и память не вызывают беспокойства, нам никогда не понадобится std::move для строки. Таким образом, любая дискуссия об этих типах std::move не должна обсуждаться с «правом собственности», ИМХО.

Juliean 28.04.2024 17:21

@Juliean Даже для вашего случая оптимизации это то же самое. Использование std::move действительно будет оптимизацией производительности. Но это также передача права собственности (даже если это не входит в ваши намерения). Исходная строка перемещена и теперь находится в неопределенном состоянии и больше отвечает за удаление буфера, выделенного в куче, поэтому семантика перемещения по-прежнему остается :)

Pepijn Kramer 28.04.2024 18:45

Немного связанный момент, который может немного изменить мировоззрение — если у вас есть доступ к C++17, вам все равно не следует использовать const string&, но string_view:

void print_str(std::string_view str) {
    std::cout << str << '\n';
}

Это не полностью отвечает на ваш вопрос, хотя и немного меняет смысл, поскольку теперь у нас даже нет ссылки на std::string, а есть общее «представление», которое может сопоставляться с чем угодно, включая строковые литералы.

print_str по-прежнему будет возможно получить перегрузку с использованием строки пересылки, если print_str когда-либо будет использовать реализацию, которая могла бы использовать семантику перемещения. Однако я считаю это довольно маловероятным, поэтому для вашего конкретного случая print_str я бы сказал: вряд ли это когда-либо будет иметь ощутимое применение для перемещения входной строки, поэтому просто используйте string_view и не перемещайтесь по вызову.

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