#c #trim #undefined-behavior #sequence-points
#c #обрезать #неопределенное поведение #точки последовательности
Вопрос:
Я хочу обрезать строку в C с помощью этого кода:
std::string str(" Trim test ");
str.erase( /* 1 */
0, /* 2 */
str.find_first_not_of(" ") /* 3 */
) /* 4 */
.erase( /* 5 */
str.find_last_not_of(" ") 1, /* 6 */
std::string::npos /* 7 */
); /* 8 */
Позволяет ли стандарт вычислять строку #6 до выполнения строки #1, чтобы при окончательном вызове #5 аргумент мог быть недействительным?
Комментарии:
1. Хорошо. Ни один из этих вопросов, связанных с вызовом / последовательностью функций… ну вот, мы снова здесь!
2. Что ж, если это так очевидно, почему бы не написать ответ?
3. Я никогда не говорил, что это очевидно. Просто то, что это продолжает всплывать — часто приводя к длительным дискуссиям / дебатам / цитированию ссылок / результатам, связанным с конкретной реализацией, и т.д., и т.п. Этот вопрос имеет особенность быть цепным методом по сравнению с «простым» вызовом функции.
Ответ №1:
Да, стандарт позволяет вычислять строку # 6 до выполнения строки # 1.
Со страницы Википедии о точках последовательности:
[Точки последовательности возникают] Перед вводом функции в вызов функции. Порядок, в котором вычисляются аргументы, не указан, но эта точка последовательности означает, что все их побочные эффекты завершаются до ввода функции. В выражении f(i ) g(j ) h(k ) вызывается f с параметром исходного значения i, но i увеличивается перед вводом тела f. Аналогично, j и k обновляются перед вводом g и h соответственно. Однако не указано, в каком порядке выполняются f(), g(), h(), а также в каком порядке i, j, k увеличиваются. Переменные j и k в теле f могут быть или не быть уже увеличены. Обратите внимание, что вызов функции f(a,b,c) не является использованием оператора запятой, и порядок вычисления для a, b и c не определен.
Вы должны переписать его как
str.erase(0, str.find_first_not_of(" "));
str.erase(str.find_last_not_of(" ") 1, std::string::npos);
Комментарии:
1. Итак, при чтении цитаты кажется, что «да» отвечает на вопрос «Разрешает ли стандарт вычислять строку # 6 перед строкой # 1», однако это можно легко спутать с ответом на «Существует ли гарантированное отношение «происходит до» […].]». Вам, вероятно, следует уточнить.
Ответ №2:
Да, 6 может быть вызвано до 1.
Однако код можно изменить так, чтобы он не имел значения:
std::string str(" Trim test ");
str.erase( /* 1 */
str.find_last_not_of(" ") 1, /* 2 */
std::string::npos /* 3 */
) /* 4 */
.erase( /* 5 */
0, /* 6 */
str.find_first_not_of(" ") /* 7 */
); /* 8 */
Нет никакой гарантии, что 7 не будет вызван до 1, но это не имеет значения, поскольку индекс все еще действителен.
Имейте в виду, что так много в одной строке — плохая идея, и должно быть не менее 4 отдельных строк.