возвращает const std :: string действительно медленнее, чем неконстантный? - PullRequest
3 голосов
/ 13 июня 2019

В другом вопросе пользователь сделал комментарий, что возвращение const std :: string теряет эффективность построения перемещения и медленнее.

Правда ли, что присвоение строки возврата этого метода:

const std::string toJson(const std::string &someText);

const std::string jsonString = toJson(someText);

... действительно медленнее, чем неконстантная версия:

std::string toJson(const std::string &str);

std::string jsonString = toJson(someText);

А в чем смысл эффективности конструкции перемещения в этом контексте?

Я никогда раньше не слышал об этом ограничении и не помню, чтобы видел это в профилировщике. Но мне любопытно.

Редактировать: Есть вопрос: Что такое семантика перемещения? . Хотя некоторые объяснения, конечно, относятся к эффективности, в нем объясняется , что означает семантика перемещения, но не рассматривается , почему возврат значения const может иметь отрицательные побочные эффекты в отношении производительности.

Ответы [ 3 ]

12 голосов
/ 13 июня 2019

Рассмотрим следующие функции:

std::string f();
std::string const g();

Разницы между ними нет:

std::string s1 = f();
std::string s2 = g();

Теперь мы гарантируем исключение копирования, в обоих случаях мы строим непосредственно врезультирующий объект.Нет копии, нет хода.

Однако существует большая разница между:

std::string s3, s4;
s3 = f(); // this is move assignment
s4 = g(); // this is copy assignment

g() может быть значением r, но это значение const r.Он не может связываться с аргументом string&&, который принимает оператор присваивания перемещения, поэтому мы возвращаемся к оператору присваивания copy , параметр string const& которого может с радостью принять значение r.

Копированиеопределенно медленнее, чем перемещение для таких типов, как string, где перемещение является постоянным временем, а копирование является линейным и может потребовать выделения.

Не возвращать константные значения.

Кроме того, для неклассных типов:

int f();
int const g();

Эти два на самом деле одинаковы, оба возвращают int.Это странная причуда языка, которую вы не можете вернуть const prvalue не-классового типа, но вы можете вернуть const prvalue типа class.Проще всего притвориться, что ты не можешь сделать последнее тоже, потому что не должен.

1 голос
/ 13 июня 2019

Без чтения спецификации или чего-либо еще, если мы просто думаем об этом логически ...

Например, допустим, у вас есть

// Declare the function
std::string const my_function();

// Initialize a non-constant variable using the function
std::string my_string = my_function();

Значение, возвращаемое функцией , может быть скопировано во временный объект, затем значение из функции уничтожается. Временный объект (который является постоянным) затем копируется в объект my_string, а затем временный объект уничтожается. Два экземпляра и два разрушения. Звучит немного чрезмерно, не правда ли? Особенно если учесть, что значение внутри функции и временного объекта будет уничтожено, поэтому им не нужно сохранять свое содержимое.

Не лучше ли было бы скопировать elided , возможно, оба? Тогда может случиться так, что значение внутри функции перемещается непосредственно в объект my_string. const статус чего-либо не имеет значения, так как объекты, с которых он перемещается, в любом случае будут уничтожены.

Последний - то, что делают современные компиляторы, они перемещаются, даже если объявлена ​​функция, возвращающая значение const. И даже если значение или объект внутри функции равен const.

0 голосов
/ 15 июня 2019

Утверждения, подобные этому, имеют определенный смысл с точки зрения инициализации,

std::string getString();
const std::string getConstantString();

std::string str = getString(); // 1
const std::string str = getConstantString(); //2

Оба оператора инициализации 1 и 2 подпадают под инициализацию копирования. Теперь это зависит от cv-qualification (const и volatile) return type, есть две возможности, если return type равно cv-unqualified и move constructor доступно для класса, тогда объект будет move initialized как в выражении 1 и если return type равно cv-qualified, то объект будет copy initialized, как в операторе 2.
Но есть оптимизация под названием copy-elision (игнорируется cv-qualification), и из-за copy-elision, объекты создаются непосредственно в хранилище, куда они в противном случае были бы скопированы / перемещены.

Существует два типа copy-elision, NRVO, "named return value optimization" и RVO, "return value optimization", но с c++17 Оптимизация возвращаемого значения является обязательной и больше не рассматривается как разрешение на копирование.
Пожалуйста, смотрите следующую ссылку copy-elision для более подробной информации.

...