Вот фрагмент, который компилируется и работает:
#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
по значению.Да, и да. Вы закончили с str
и указываете, передав право собственности, что print_str
может использовать его, если это необходимо (и вам нужно явно передать право собственности, в этом случае компилятор не сможет сделать это за вас, несмотря на то, что это «очевидно»). Тот факт, что этого не происходит, является деталью реализации, и это может измениться через день, месяц или год. Благодаря этой семантике C++11 оставался совместимым со всем существующим кодом C++03.
В C++ не рекомендуется всегда что-то делать, даже если вы не знаете, почему.
@BoP - Тем более, когда ты не знаешь почему.
1. Это вредно для читателей этого кода :) - когда кто-то читает код, содержащий std::move, он думает, что вы хотите использовать конструктор перемещения. 2. Почему print_str будет изменен, чтобы получить std::string по значению? :) Лучше использовать string_view.
Вы правы, move
здесь не имеет никакого эффекта, и вопрос о том, использовать ли это, полностью зависит от вашего мнения.
С одной стороны, это действительно может улучшить производительность, если/когда print_str
изменить таким образом. Он также выражает намерение больше не использовать str
.
С другой стороны, это может быть момент WTF для будущего читателя вашего кода. Кроме того, если после этого у вас есть код, который (неправильно) считывается из str
после бездействия, if сломается, когда print_str
обновится таким образом.
Лично я бы сделал это только если:
print_str
выглядит так, будто он должен принимать аргумент по значению, но в настоящее время этого не происходит (например, установщик, который должен переместить строку в класс, но в настоящее время копирует ее)Вы говорите, что это основано на мнениях, но не все мнения имеют одинаковую ценность. Вот в чем дело: использование str
после std:move
по ошибке на самом деле не является проблемой. Это можно пометить как с помощью проверки кода, так и с помощью инструментов. WTF для менее опытных также не является аргументом, поскольку это предотвратило бы 90% эволюции C++. Мы не можем вечно писать код с наименьшим общим знаменателем.
@StoryTeller-UnslanderMonica Видимо, мы можем продолжать писать к наименьшему общему знаменателю вечно - я только что боролся с некоторыми VBA в Excel.
Я не согласен с «мнением, основанным здесь». Написание хорошего кода также подразумевает выражение намерений. Если вы не собираетесь передавать право собственности, не следует использовать std::move (даже если это всего лишь приведение типов)
@PepijnKramer Это один из пунктов этого ответа. Вы намерены передать право собственности «если/когда print_str
будет изменено [принять std::string&&
]»
@PepijnKramer std::move
не всегда выражает передачу права собственности. В шаблоне это вполне может выражаться: «Я больше не буду использовать этот объект, вы можете делать с ним все, что захотите» (т. е. «передать право собственности, если применимо»). Например. если у вас есть шаблон с std::move
, а затем создайте его экземпляр для int
, вы наверняка не будете беспокоиться о бесполезном движении. Если нас устраивает значение move
в шаблонах, я думаю, что вне шаблонов оно не обязательно плохо.
Два человека не согласны с «основанным на мнениях» и поддерживают разные мнения. :П
@StoryTeller-UnslanderMonica Фактор WTF меня тоже не остановил бы, но я бы добавил короткий комментарий, объясняющий, почему здесь есть move
.
@HolyBlackCat - я уверен, что к тому времени, когда этот вопрос станет горячим в сети, не будет N несогласных людей и N + 1 мнений :p
Читая комментарии, могу согласиться, что это основано на мнениях :) Кстати, я также обнаружил, что статический анализатор PVS-Studio показывает предупреждение для такого рода std::move
(внизу страницы).
@PepijnKramer Передача права собственности для меня не имеет смысла для std::string. Право собственности на что? Буфер, выделенный в куче, которым случайно владеет std::string, если он превышает предел SSO? std::move с определенными типами, такими как string, предназначен для повышения производительности, просто и понятно. Семантическая передача владения имеет смысл для типов, которые концептуально владеют чем-то, например std::unique_ptr. В мире, где производительность и память не вызывают беспокойства, нам никогда не понадобится std::move для строки. Таким образом, любая дискуссия об этих типах std::move не должна обсуждаться с «правом собственности», ИМХО.
@Juliean Даже для вашего случая оптимизации это то же самое. Использование std::move действительно будет оптимизацией производительности. Но это также передача права собственности (даже если это не входит в ваши намерения). Исходная строка перемещена и теперь находится в неопределенном состоянии и больше отвечает за удаление буфера, выделенного в куче, поэтому семантика перемещения по-прежнему остается :)
Немного связанный момент, который может немного изменить мировоззрение — если у вас есть доступ к 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
и не перемещайтесь по вызову.
Нет, это не будет хорошей практикой. Вы используете
std::move
, чтобы указать на передачу права собственности и на то, что вы хотите вызватьprint_str(std::string&& str)
. Передача по ref указывает на то, что передачи права собственности быть не должно.