Подтвердить что ты не робот

Можно ли добавить std::string в себя?

Учитывая такой код:

std::string str = "abcdef";
const size_t num = 50;

const size_t baselen = str.length();
while (str.length() < num)
    str.append(str, 0, baselen);

Безопасно ли вызывать std::basic_string<T>::append() как таковое? Не удается ли потерять память источника, увеличив ее до операции копирования?

Я не мог найти ничего в стандарте, специфичном для этого метода. Он говорит, что приведенное выше эквивалентно str.append(str.data(), baselen), что, я думаю, может быть не совсем безопасным, если не будет обнаружение таких случаев внутри append(const char*, size_t).

Я проверил несколько реализаций, и они казались безопасными так или иначе, но мой вопрос в том, гарантируется ли это поведение. Например. "Добавление std::vector к себе, undefined поведение?" говорит об этом не для std::vector.

4b9b3361

Ответ 1

Согласно § 21.4.6.2/§21.4.6.3:

Функция [ basic_string& append(const charT* s, size_type n);] заменяет строку, контролируемую * этим, строкой length size() + n, чьи первые элементы size() являются копией исходной строки, контролируемой * этот и оставшиеся элементы которого являются копией начальных n элементов s.

Примечание. Это относится ко всем вызовам append, так как каждый append может быть реализован в терминах append(const charT*, size_type), как определено стандартом (§21.4.6.2/§21.4.6.3).

Таким образом, append делает копию str (позвоните по вызову strtemp), добавляет n символы от str2 до strtemp, а затем заменяет str на strtemp.

В случае, когда str2 является str, ничто не изменяется, поскольку строка увеличивается при назначении временной копии, а не раньше.

Несмотря на то, что это не указано явно в стандарте, оно гарантируется (если реализация в точности соответствует заявленной в стандарте) по определению std::basic_string<T>::append.

Таким образом, это не поведение undefined.

Ответ 2

Это сложно.

Одно можно сказать наверняка. Если вы используете итераторы:

std::string str = "abcdef";
str.append(str.begin(), str.end());

тогда вы гарантированно будете в безопасности. Да, действительно. Зачем? Поскольку в спецификации указано, что поведение функций итератора эквивалентно вызову append(basic_string(first, last)). Это, очевидно, создает временную копию строки. Поэтому, если вам нужно вставить строку в себя, вы гарантированно сможете сделать это с помощью формы итератора.

Конечно, реализациям не нужно ее копировать. Но они должны соблюдать стандартное поведение. Реализация может сделать копию только в том случае, если диапазон итератора находится внутри себя, но реализация все равно должна быть проверена.

Все остальные формы append определены как эквивалентные вызову append(const charT *s, size_t len). То есть ваш призыв к приложению выше эквивалентен тому, что вы делаете append(str.data(), str.size()). Итак, что говорит стандарт о том, что произойдет, если s находится внутри *this?

Ничего.

Единственное требование s:

s указывает на массив не менее n элементов charT.

Так как он явно не запрещает s указывать на *this, то он должен быть разрешен. Было бы также чрезвычайно странно, если версия итератора допускает самозадачу, но версия указателя и размера не имеет значения.