2016-09-28 7 views
4

Я следующий код, я использую GCC и C++ 11:Инициализация std :: string с возвращаемым значением функции, есть ли копия?

std::string system_call(const char *cmd){ 
    std::string a; 
    ... 
    return a; 
} 

std::string st = system_call("whatever code"); 

Есть ли неявная копия есть? Я вызываю эту функцию много раз, и я думаю, что она делает копию из возвращаемого значения system_call в переменную st, а затем освобождает временное значение r.

Есть ли способ избежать копирования? Использование st.swap(system_call()) броски и ошибки в компиляторе:

error: no matching function for call to 'std::basic_string::swap(std::string)'

Мои вопросы:

  1. Если есть проблема или нет
  2. Как избежать этого, если есть один

Спасибо

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

system_call("whatever").swap(st); 

ответ

13

В общем случае, если в вызове сконфигурировано std::string, а затем вернулись самые современные компиляторы с применением оптимизации возвращаемого значения (частный случай copy elision). В частности, ваше дело - это Именованное RVO (спасибо @NathanOliver за указание этого), если вы хотите найти точные правила. С C++ 17 эта оптимизация гарантируется стандартом.

Невозможно сказать, применяется ли оптимизация, однако, если это не так, то в C++ 11 и выше очень вероятно, что объект std::string в рамках функции будет перемещен из и возвращаемое значение будет перенесено в. Теоретически вы можете называть std::move возвращаемым значением, но тем самым вы также предотвращаете любое RVO. Хотя перемещение может быть эффективным, RVO обычно дает более быстрый код, потому что ничего не перемещается и не копируется вообще, но объект построен на месте, поэтому я бы посоветовал не делать этого и в пользу использования вашего компилятора.

+0

Хорошо знать, что компилятор уже может это сделать. Но разве я могу заставить его это сделать? Swap, похоже, не работает, так как ему нужна не временная ссылка. – DarkZeros

+0

Вы можете вызвать 'std :: move' в возвращаемом значении, но это предотвратит копирование по правилам стандарта, что еще более эффективно, чем перемещение. Если применяется небольшая оптимизация строк, перемещение из строки может, например, быть не намного эффективнее, если вообще. Я бы посоветовал не переезжать. Также, где вы хотите позвонить на своп? Личная оценка заключается в том, что возвращение по стоимости и полагаться на RVO позволит получить наиболее эффективное решение здесь. – midor

+0

@DarkZeros Невозможно заставить компилятор не реализовывать 'a + = b' как' a = 0; for (int i = 0; i Yakk

3

Поскольку вопрос помечается как c++11, я предполагаю, что вы компиляции кода с c++11 компилятором.

В этом случае значение, возвращаемое system_call, не будет скопировано. Компилятор может переместить конструкцию st с возвращаемым значением или использовать Оптимизацию возвращаемого значения для удаления копии. В любом случае копия не будет.

+1

конкретно это NRVO, который немного сложнее оптимизировать. – NathanOliver

4

Elision - это разрешение, предоставляемое разработчикам для использования несколькими значениями share, когда они кажутся разными значениями в коде.

std::string system_call(const char *cmd){ 
    std::string a; 
    ... 
    return a; // all return paths return `a` directly 
} 

std::string st = system_call("whatever code"); 

В приведенном выше случае, элизия означает, что a, возвращаемое значение system_call и stвсе тот же объект.

Современные компиляторы elide, если вы не дадите им патологический флаг, чтобы сказать «не elide», когда это допускается стандартом и кодом. «Что делать, если это невозможно, когда это возможно» - это как спросить, что, если компилятор реализует целочисленное добавление в виде зацикленного приращения.

Оба варианта разрешены стандартом, и разумно ожидать.

Когда elision терпит неудачу (потому что ваш код делает невозможным), он возвращается к семантике перемещения в C++ 11. Для std::string это означает, что при перемещении не происходит выделения памяти; в случае небольшой оптимизации строки некоторое количество символов может быть скопировано.

Elision может выйти из строя, если ваш код может вернуть заданную именованную переменную вдоль одного пути, а другой - по другому пути. Или, если вы вернете параметр функции.

Elision разрешен, если ваш оператор возврата return named_variable; или return some_temporary_object;, где возвращаемый объект соответствует типу возвращаемой функции. Это также разрешено, если вы делаете some_type bob = some_temporary;, где some_temporary является временным объектом типа some_type. Аналогичным образом вы можете перейти в аргумент функции (но не из него).

Невозможно «гарантировать» разрешение.

C++ 17 делает почти все исключения из временных исключений выше обязательных.

Для того чтобы разрешить работу на C++ 14 и раньше, должен быть экземпляр или перемещать конструктор. Когда возникает элиция, она не называется, но она должна существовать. В C++ 17, когда временные «силы» устранены, конструктор копирования или перемещения не существует: временный объект не является отдельным объектом, а скорее предложением, которое представляет «как построить объект», что делается только позже.

 Смежные вопросы

  • Нет связанных вопросов^_^