Разница между строкой + = s1 и строкой = строка + s1
Одна из моих программ превышает ограничение по времени, когда я использую fans = fans + s[i]
, а когда я использую fans += s[i]
это принимается... Почему это происходит? Чтобы объяснить больше, fan - это строка, а s - это тоже строка, поэтому при переборе по строке s я хочу только несколько символов s, поэтому я создаю новую строку fan. Теперь есть два способа добавить символ в мою новую строку фанаты. Проблема упоминается ниже
fans = fans + s[i]; // gives Time limit exceeded
fans += s[i]; // runs successfully
Ответы
Ответ 1
Для встроенных типов a += b
в точности совпадает с a = a + b
, но для классов эти операторы перегружены и вызывают разные функции.
В вашем примере fans = fans + s[i]
создает временную строку и назначает (перемещает) ее fans
, но fans += s[i]
не создает эту временную строку, поэтому она может быть быстрее.
Ответ 2
std::string
есть члены operator +
и operator +=
. Первый обычно реализуется с последним в качестве промежуточного временного. Выглядит примерно так (проверьте источник реализации, если хотите точно знать, что делает ваш):
/// note reference return type
std::string& operator +=(char c)
{
this->append(c);
return *this;
}
// note value return type
std::string operator +(char c) const
{
std::string tmp = *this;
tmp += c; // or just tmp.append(c) directly
return tmp;
}
Настройка tmp
стоит дорого. Общая функция может (и обычно так) улучшается с помощью семантики назначения перемещения в конечный пункт назначения на стороне вызывающей стороны, но тем не менее затраты на временные функции все же остаются. Сделайте это несколько раз, и вы не заметите разницу. Делайте это тысячи или миллионы раз, и это может означать мир различий.
Ответ 3
Если вы используете fans=fans+s[i]
, строка будет копироваться при каждом проходе цикла. Новый элемент будет добавлен в копию строки, а результат будет переназначен переменной fans
. После этого старая строка должна быть удалена, потому что на нее больше нет ссылок. Это занимает много времени.
Если вы используете расширенное присваивание fans+=s[i]
строка не будет копироваться при каждом проходе цикла, и нет необходимости удалять ссылочную переменную, поскольку здесь нет ссылочной переменной. Это экономит много времени.
Я надеюсь, теперь вы можете понять!
Ответ 4
Здесь есть полезная книга по этому вопросу:
https://www.oreilly.com/library/view/optimized-c/9781491922057/ch04.html
"fan = fan + s [i]" в 13 раз медленнее, чем "fan + = s [i]" !!
Ответ 5
Для фундаментальных типов a = a + b
и a += b
означают одно и то же.
Для произвольных типов классов a = a + b
и a += b
не связаны; они ищут разных операторов, и эти операторы могут делать произвольные вещи. То, что они на самом деле не связаны, - это запах кода, признак проблемы дизайна.
a = a + b
становится operator=( a, operator+( a, b ) )
примерно; фактические правила поиска немного сложнее (включая операторы-члены и операторы, не являющиеся членами, и тот факт, что =
не имеет оператора, не являющегося членом, и т.д.), но это является его ядром.
a += b
становится operator+=( a, b )
в аналогичном смысле.
Теперь это общий шаблон для реализации +
в терминах +=
; если вы сделаете это, вы получите:
a = a + b
становится
a = ((auto)(a) += b);
где (auto)
- это новая функция "создать временную копию аргумента" c++20/c++23.
По сути, a+=b
может повторно использовать содержимое a
напрямую, тогда как a = a + b
не может; в настоящий момент a+b
оценивается, он не знает, что a
скоро будет перезаписано.
Некоторые библиотеки имеют дело с этим, используя технику, известную как "шаблоны выражений"; a+b
- это не значение, а описание во время компиляции выражения a+b
, которое при присвоении a
фактически используется для заполнения a
данными. С помощью шаблонов выражений a+=b
фундаментальная проблема, a+=b
в том, что a+=b
зная больше, чем a=a+b
.
Теперь, в частности, для std::string
, a+b
создает объект временной строки, затем a=(a+b)
перемещает его в a
(он может повторно использовать буфер объекта временной строки или буфер a
, стандарт молчит в этом вопросе).
a+=b
должны повторно использовать любой избыток мощности в a
буфера. Так что если вы a.reserve(1<<30)
(1 млрд), a+=b
не может выделить больше.