Чи є `string.assign (string.data (), 5)` чітко визначеним або UB?


11

Співробітник хотів написати це:

std::string_view strip_whitespace(std::string_view sv);

std::string line = "hello  ";
line = strip_whitespace(line);

Я сказав, що повернення string_viewзробило мене неспокійно апріорі , і, крім того, псевдонім тут для мене був схожим на UB.

Я можу з упевненістю сказати, що line = strip_whitespace(line)в даному випадку рівнозначно line = std::string_view(line.data(), 5). Я вважаю, що виклик string::operator=(const T&) [with T=string_view], який визначається як еквівалентний line.assign(const T&) [with T=string_view], який визначений як еквівалентний line.assign(line.data(), 5), який визначено для цього:

Preconditions: [s, s + n) is a valid range.
Effects: Replaces the string controlled by *this with a copy of the range [s, s + n).
Returns: *this.

Але це не говорить про те, що відбувається, коли відбувається згладжування.

Я поставив це запитання на cpplang Slack вчора і отримав неоднозначні відповіді. Тут шукають супер авторитетні відповіді та / або емпіричний аналіз реальної реалізації бібліотечних постачальників.


Я написав тестові випадки для string::assign, vector::assign, deque::assign, list::assign, і forward_list::assign.

  • Libc ++ змушує усі ці тестові справи працювати.
  • Libstdc ++ змушує їх працювати, за винятком тих forward_list, які переходять на сегменти .
  • Я не знаю про бібліотеку MSVC.

Segfault в libstdc ++ дає мені надію, що це UB; але я також бачу, що і libc ++, і libstdc ++ докладають великих зусиль, щоб зробити цю роботу хоча б у звичайних випадках.


Ви склали тестові приклади з ASan та / або запустили їх під Valgrind? Це призведе до того, що код може спричинити порушення доступу, хоча він може все-таки працювати на практиці, а не за визначенням.
Конрад Рудольф

1
"Якщо якась функція-член або оператор basic_string кидає виняток, ця функція або оператор не має іншого впливу на об'єкт basic_string." - це змушує розподіл пам’яті відбуватись до звільнення існуючого сховища, так що викид викидається, якщо розподіл не вдасться, не змінюючи *this. Але я не бачу нічого, що б перешкоджало повторному використанню наявного сховища, і в цьому випадку це стає не визначеним, оскільки семантика копіювання над сховищем не визначена.
Сем Варшавчик


2
Для згаданих контейнерів послідовностей це, безумовно, UB через порушення попередніх умов assignвимог у [tab: container.seq.req] .
волоський горіх

Відповіді:


8

Якщо заборонити пару винятків, у яких ваше не є одним, виклик функції non-const (тобто assign) у рядку недійсно [...] вказує [...] на його елементи. Це порушує попередню умову про assignте , що [s, s + n)це допустимий діапазон, так що це невизначене поведінку.

Зверніть увагу, що string::operator=(string const&)мова має спеціально для того, щоб зробити самопризначення неоперативним.


1
Тож, що саме є точкою недійсності та точкою, в якій необхідна передумова? Здається, відповідь передбачає, що попередня умова повинна виконуватися після виклику функції члена.
волоський горіх

1
@walnut Я не юрист з мови (ані людина з особливо розширеними знаннями на C ++), але коли ми обернемо ваш сценарій, ми можемо задати питання - чи міг би бути визнаний недійсний діапазон під час виконання assign? Якщо так, то нам би довелося встановити певну точку в рамках реалізації присвоєння, щоб позначити, коли саме може статися інвалідність, і я вважаю, що це не те, що C ++ зробить. Я можу помилитися.
Fureeish

2
@Fureeish я також не знаю, але див., Наприклад, випуск 526 LWG , закритий як " не дефект ", який у своїй рекомендації щодо закриття зазначає, що std::vector::insert(iterator pos, const T& value)повинен працювати, якщо valueзнаходиться у самому векторі, оскільки стандарт не визначає, що це не може працювати, навіть якщо ця посилання може бути визнана недійсною при виклику.
волоський горіх

1
@walnut « потрібно на роботу , тому що стандарт не дає дозволу на це не працювати. » - любити його . Sooo ... чи варто запитати, що відбувається на практиці ? Чи потрібна реалізація, щоб зробити копію аргументу в такій ситуації? Як ти реально це міг реалізувати ..? Я чув про стандартні вимоги компіляторів зробити неможливе - це один із таких випадків? Незалежно, дякую за коментар!
Fureeish

1
@Fureeish Насправді мій попередній (тепер видалений) приклад насправді не перевіряв те, що я хотів перевірити. Ось фіксований приклад, що показує, що і libc ++, і libstdc ++ насправді копіюють перед тим, як переходити до перерозподілу за потребою.
волоський горіх
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.