Это должно работать:
box2.appendChild(box1);
Herb Sutter все еще записывается вместе с Bjarne Stroustroup, рекомендуя const std::string&
как тип параметра; см. https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rf-in .
В любом из других ответов здесь нет ни малейшей ошибки: если вы передадите строковый литерал в параметр const std::string&
, он передаст ссылку на временную строку, созданную на лету для хранения символов буквального. Если вы сохраните эту ссылку, она будет недействительной после освобождения временной строки. Чтобы быть в безопасности, вы должны сохранить копию , а не ссылку. Проблема связана с тем, что строковые литералы - это типы const char[N]
, для которых требуется продвижение до std::string
.
Код ниже иллюстрирует ловушку и обходной путь вместе с небольшим вариантом эффективности - перегрузка с помощью метода const char*
, как описано в . Есть ли способ передать строковый литерал как ссылку в C ++ .
(Примечание: Sutter & amp; Stroustroup советует, чтобы, если вы сохраняете копию строки, также предоставляете перегруженную функцию параметром & amp; & amp; и std :: move ( ) it.)
#include <string>
#include <iostream>
class WidgetBadRef {
public:
WidgetBadRef(const std::string& s) : myStrRef(s) // copy the reference...
{}
const std::string& myStrRef; // might be a reference to a temporary (oops!)
};
class WidgetSafeCopy {
public:
WidgetSafeCopy(const std::string& s) : myStrCopy(s)
// constructor for string references; copy the string
{std::cout << "const std::string& constructor\n";}
WidgetSafeCopy(const char* cs) : myStrCopy(cs)
// constructor for string literals (and char arrays);
// for minor efficiency only;
// create the std::string directly from the chars
{std::cout << "const char * constructor\n";}
const std::string myStrCopy; // save a copy, not a reference!
};
int main() {
WidgetBadRef w1("First string");
WidgetSafeCopy w2("Second string"); // uses the const char* constructor, no temp string
WidgetSafeCopy w3(w2.myStrCopy); // uses the String reference constructor
std::cout << w1.myStrRef << "\n"; // garbage out
std::cout << w2.myStrCopy << "\n"; // OK
std::cout << w3.myStrCopy << "\n"; // OK
}
OUTPUT:
const char * constructor const std::string& constructor Second string Second string
Если вам действительно нужна копия, все равно разумно принять const &
. Например:
bool isprint(std::string const &s) {
return all_of(begin(s),end(s),(bool(*)(char))isprint);
}
Если вы измените это значение, чтобы взять строку по значению, вы в конечном итоге перемещаете или копируете параметр, и в этом нет необходимости. Скорее всего, не только копирование / перемещение скорее дороже, но и новый потенциальный сбой; копия / перемещение может вызвать исключение (например, распределение во время копирования может завершиться с ошибкой), тогда как ссылка на существующее значение не может быть.
Если вам do нужна копия, тогда передача и возврат по значению обычно (всегда?) лучший вариант. На самом деле я вообще не стал бы беспокоиться об этом в C ++ 03, если вы не обнаружите, что дополнительные копии фактически вызывают проблемы с производительностью. Копирование elision кажется довольно надежным для современных компиляторов. Я думаю, что скептицизм и настойчивость людей в том, что вы должны проверить свою таблицу поддержки компилятора для RVO, в большинстве случаев устарели.
Короче говоря, C ++ 11 ничего не меняет в этом отношении, кроме для людей, которые не доверяли копированию.
noexcept
, но конструкторы копирования, очевидно, не являются.
– leftaroundabout
19 April 2012 в 16:53
Это сильно зависит от реализации компилятора.
Однако это также зависит от того, что вы используете.
Давайте рассмотрим следующие функции:
bool foo1( const std::string v )
{
return v.empty();
}
bool foo2( const std::string & v )
{
return v.empty();
}
Эти функции реализованы в отдельном блоке компиляции, чтобы избежать встраивания. Затем: 1. Если вы передадите литерал для этих двух функций, вы не увидите большой разницы в характеристиках. В обоих случаях должен быть создан строковый объект 2. Если вы передадите другой объект std :: string, foo2
превзойдет foo1
, потому что foo1
сделает глубокую копию.
Вкл. мой компьютер, используя g ++ 4.6.1, получил следующие результаты:
Почти.
В C ++ 17 мы имеем basic_string_view<?>
, что сводит нас к основному узкому варианту использования для параметров std::string const&
.
Существование move semantics удалила один прецедент для std::string const&
- если вы планируете сохранить параметр, использование std::string
по значению более оптимально, так как вы можете move
выйти из параметра.
Если кто-то назвал вашу функцию сырым C "string"
, это означает, что только один std::string
буфер когда-либо выделен, в отличие от двух в случае std::string const&
.
Однако, если вы этого не сделаете намеревается сделать копию, взятие std::string const&
по-прежнему полезна в C ++ 14.
С std::string_view
, если вы не передаете указанную строку API, который ожидает C-стиль '\0'
-термированные буферы символов, вы можете более эффективно получать функциональные возможности std::string
, не рискуя распределением. Необработанную строку C можно даже превратить в std::string_view
без какого-либо выделения или копирования символов.
В этот момент использование для std::string const&
происходит, когда вы не копируете данные по оптовой торговле и собирается передать его API-интерфейсу C, который ожидает буфер с нулевым завершением, и вам нужны строковые функции более высокого уровня, которые предоставляет std::string
. На практике это редкий набор требований.
IMO, использующая ссылку C ++ для std::string
, представляет собой быструю и короткую локальную оптимизацию, а использование передачи по значению может быть (или нет) лучшей глобальной оптимизацией.
Итак, ответ таков: это зависит от обстоятельств:
const std::string &
. std::string
. Проблема заключается в том, что «const» является неграмотным классификатором. То, что обычно подразумевается под «const string ref», это «не изменять эту строку», а не «не изменять счетчик ссылок». В C ++ просто нет способа сказать , что члены являются «const». Они либо все, либо ни один из них.
Чтобы взломать эту проблему языка, STL может разрешить «C ()» в вашем примере, чтобы в любом случае сделать семантическую копию перемещения , и покорно игнорируют «const» в отношении счетчика ссылок (изменяемый). Пока это было четко указано, все будет хорошо.
Поскольку STL этого не делает, у меня есть версия строки, которая const_casts & lt;> удаляет ссылочный счетчик (без возможности ретроактивно сделать что-то изменчивое в иерархии классов), и - lo and behold - вы можете свободно передавать cmstring в качестве ссылок на const и делать копии их в глубоких функциях в течение всего дня без каких-либо утечек или проблем.
Поскольку C ++ не предлагает «гранулярность производного класса» здесь, написав хорошую спецификацию и создание блестящего нового объекта const constable (cmstring) - лучшее решение, которое я видел.
См. «Herb Sutter» Назад к основам! Основы современного стиля C ++ ». Среди других тем он рассматривает передаваемый параметр, который был дан в прошлом, и новые идеи, которые приходят в с C ++ 11 и специально рассматривает идею передачи строк по значению.
[/g4]
Тесты показывают, что передача std::string
s по значению, в случаях, когда функция будет копировать его в любом случае, может быть значительно медленнее!
Это потому, что вы вынуждаете его всегда делать полную копию (а затем перемещаться на место), а версия const&
обновит старую строку, которая может повторно использовать уже выделенный буфер.
См. его слайд 27: Для «заданных» функций опция 1 такая же, как и всегда. Вариант 2 добавляет перегрузку для ссылки rvalue , но это дает комбинаторный взрыв, если имеется несколько параметров.
Только для параметров «sink», где должна быть создана строка (не изменилось ее существующее значение), что трюк с пропуском va lid, то есть конструкторы, в которых параметр непосредственно инициализирует член типа соответствия.
Если вы хотите увидеть, как глубоко вы можете беспокоиться об этом, посмотрите Nicolai Josuttis и удачи с этим ( «Perfect-Done!» n раз после обнаружения ошибки в предыдущей версии. Когда-либо там?)
Это также суммируется как ⧺F.15 в Стандартных правилах.
Короткий ответ: НЕТ! Длинный ответ:
const ref&
. (const ref&
, очевидно, должно оставаться в пределах область действия, в то время как исполняемая функция выполняет) value
, не копируйте const ref&
внутри вашего тела функции. Была запись на cpp-next.com под названием «Требуется скорость, перейдите по значению! «. TL; DR:
Guideline: не копируйте аргументы функции. Вместо этого передайте их по значению и пусть компилятор выполнит копирование.
ПЕРЕВОД ^ ^ / / 10]
Не копируйте аргументы функции --- означает: если вы планируете изменить значение аргумента, скопировав его во внутреннюю переменную, просто используйте аргумент значения вместо .
Итак, не делайте этого:
std::string function(const std::string& aString){ auto vString(aString); vString.clear(); return vString; }
выполните следующее:
std::string function(std::string aString){ aString.clear(); return aString; }
Когда вам нужно изменить значение аргумента в своем теле функции.
Вам просто нужно знать, как вы планируете использовать аргумент в теле функции. Только для чтения или NOT ... и если он находится в пределах видимости.
const ref&
во внутреннюю переменную, чтобы изменить ее. Если вам нужно изменить его ... сделайте параметр значением. Это довольно ясно для моего неанглийского языка.
– CodeAngry
23 August 2013 в 20:16
const ref&
. # 2 Если мне нужно написать это или я знаю, что он выходит из сферы действия ... Я использую значение. # 3 Если мне нужно изменить исходное значение, я прохожу мимо ref&
. # 4 Я использую pointers *
, если аргумент не является обязательным, поэтому я могу nullptr
его.
– CodeAngry
23 August 2013 в 20:39
Являются ли дни прохождения const std :: string & amp; как параметр над?
blockquote>Нет. Многие люди берут этот совет (включая Дейва Абрахама) за пределы области, к которой он применим, и упрощают его для применения к всем параметрам
std::string
- Всегда , проходящему черезstd::string
значение не является «лучшей практикой» для любых произвольных параметров и приложений, поскольку оптимизация этих обсуждений / статей сосредоточена на применении только к ограниченному набору случаев .Если вы 're возвращающее значение, изменяя параметр или принимая значение, тогда передача по значению может сэкономить дорогое копирование и предложить синтаксические удобства.
Как и прежде, передача константой const значительно экономит , когда вы не нужно копировать .
Теперь к конкретному примеру:
Однако inval все еще намного больше размера ссылки (которая обычно реализуется как указатель). Это связано с тем, что std :: string имеет различные компоненты, включая указатель на кучу и член char [] для оптимизации коротких строк. Поэтому мне кажется, что переход по ссылке - это хорошая идея. Может ли кто-нибудь объяснить, почему Herb мог бы это сказать?
blockquote>Если размер стека является проблемой (и если это не указано / оптимизировано),
return_val
+inval
>return_val
- - IOW, использование пикового стека может быть уменьшено , пройдя здесь по значению (обратите внимание: упрощение ABI). Между тем, передача по ссылке const может отключить оптимизацию. Основная причина здесь заключается не в том, чтобы избежать роста стека, а в том, чтобы обеспечить оптимизацию , где это применимо .Дни прохода по ссылке const не превышают - - правила были сложнее, чем когда-то. Если производительность важна, вам будет разумно рассмотреть, как вы передаете эти типы, на основе сведений, которые вы используете в своих реализациях.
Как отмечает @ JDługosz в комментариях, Херб дает другие советы в другом (позже?) разговоре, см. примерно отсюда: https://youtu.be/xnqTKD8uD64?t=54m50s .
Его совет сводится только к использованию параметров значения для функции f
, которая принимает так называемые аргументы стока, предполагая, что вы переместите конструкцию из этих аргументов приемника.
Этот общий подход только добавляет накладные расходы конструктора перемещения для аргументов lvalue и rvalue по сравнению с оптимальной реализацией f
с учетом значений lvalue и rvalue соответственно. Чтобы понять, почему это так, предположим, что f
принимает параметр значения, где T
- это некоторая копия и перемещение конструктивного типа:
void f(T x) {
T y{std::move(x)};
}
Вызов f
с аргументом lvalue приведет к конструктор копирования, вызываемый для построения x
, и конструктор перемещения, вызываемый для построения y
. С другой стороны, вызов f
с аргументом rvalue приведет к вызову конструктора перемещения для построения x
и другого конструктора перемещения, который будет вызываться для построения y
.
В общем, оптимальная реализация f
для аргументов lvalue выглядит следующим образом:
void f(const T& x) {
T y{x};
}
В этом случае для построения y
вызывается только один конструктор копирования. Оптимальная реализация f
для аргументов rvalue, опять-таки, в общем случае выглядит следующим образом:
void f(T&& x) {
T y{std::move(x)};
}
В этом случае для построения y
вызывается только один конструктор перемещения.
Таким образом, разумный компромисс заключается в том, чтобы взять параметр значения и иметь один дополнительный вызов конструктора для аргументов lvalue или rvalue относительно оптимальной реализации, что также является советом, данным в разговоре Херба.
As @ JDługosz отметил в комментариях, передача по значению имеет смысл только для функций, которые будут строить какой-либо объект из аргумента приемника. Когда у вас есть функция f
, которая копирует свой аргумент, подход с поправкой будет иметь дополнительные накладные расходы, чем общий метод pass-by-const-reference. Метод pass-by-value для функции f
, сохраняющий копию его параметра, будет иметь следующий вид:
void f(T x) {
T y{...};
...
y = std::move(x);
}
В этом случае есть построение копии и назначение перемещения для аргумент lvalue и конструкцию перемещения и назначение перемещения для аргумента rvalue. Наиболее оптимальным аргументом для аргумента lvalue является:
void f(const T& x) {
T y{...};
...
y = x;
}
Это сводится только к заданию, которое потенциально намного дешевле, чем конструктор копирования, плюс назначение перемещения, требуемое для подхода с переходом по значению. Причина этого заключается в том, что назначение может повторно использовать существующую выделенную память в y
и, следовательно, предотвращать (де) распределения, тогда как конструктор копирования обычно выделяет память.
Для аргумента rvalue наиболее оптимальная реализация для f
, который сохраняет копию, имеет вид:
void f(T&& x) {
T y{...};
...
y = std::move(x);
}
Таким образом, в этом случае назначается только перемещение. Передача rvalue в версию f
, которая принимает ссылку const, стоит только присваивание вместо назначения перемещения. Так, условно говоря, предпочтительнее вариант f
, принимающий const-ссылку в этом случае как общую реализацию.
Итак, в общем случае для наиболее оптимальной реализации вам нужно будет перегрузить или сделать что-то вроде идеальной пересылки, как показано в разговоре. Недостатком является комбинаторный взрыв в количестве требуемых перегрузок, в зависимости от количества параметров для f
, если вы решите перегрузить класс значений аргумента. Идеальная пересылка имеет тот недостаток, что f
становится функцией шаблона, которая препятствует ее созданию в виртуальном режиме и приводит к значительно более сложному коду, если вы хотите получить его на 100% вправо (см. Разговор для деталей gory).
moved
от B до C в случайном случае? Если B являетсяB(std::string b)
, а C являетсяC(std::string c)
, то либо мы должны называтьC(std::move(b))
в B, либоb
должны оставаться неизменными (таким образом, «не отодвинуты от») до выходаB
. (Возможно, оптимизирующий компилятор будет перемещать строку под правилом as-if, еслиb
не используется после вызова, но я не думаю, что есть сильная гарантия.) То же самое верно для копииstr
кm_str
. Даже если параметр функции был инициализирован с rvalue, он является значением l внутри функции, иstd::move
требуется для перехода от этого lvalue. – Pixelchemist 16 July 2015 в 11:04T&&
не всегда является универсальной ссылкой; на самом деле,std::string&&
всегда будет ссылкой на rvalue и никогда не будет универсальной ссылкой, потому что не выполняется никакого вывода типа . Таким образом, Stroustroup & amp; Совет Саттера не противоречит Мейерсу. – Justin Time 17 April 2017 в 22:12T&&
выведенной универсальной ссылкой или не выводимой ссылкой rvalue; вероятно, было бы яснее, если бы они представили другой символ для универсальных ссылок (например,&&&
, как комбинацию&
и&&
), но это, вероятно, просто выглядело бы глупо. – Justin Time 19 April 2017 в 21:50std::string<>
- 32 байта, которые выделены из стека, 16 из которых должны быть инициализированы. Сравните это с 8 байтами, выделенными и инициализированными для ссылки: это вдвое больший объем работы ЦП, и он занимает в четыре раза больше пространства кеша, которое не будет доступно для других данных. – cmaster 19 June 2018 в 22:10