Почему добавление int к неопределенному поведению std::string без предупреждения компилятора в C++?

В своем коде я использую протоколирование заявлений, чтобы лучше видеть, что происходит. Иногда я пишу код, подобный следующему:

int i = 1337;
// More stuff...
logger->info("i has the following value: " + i);

При компиляции и выполнении в режиме отладки это не выводит i, как ожидалось (например, так это будет работать в Java/C#), а выводит что-то искаженное. Однако в режиме выпуска это может привести к сбою всего приложения. Что говорит стандарт C++ о добавлении целых чисел к std::string, как я делаю здесь?

Почему компилятор вообще не предупреждает меня, когда я компилирую код, вызывающий очевидное неопределенное поведение? Я что-то упускаю? Я использую Visual Studio 2022 (MSVC). Правильный способ сделать оператор ведения журнала - явно преобразовать int в std::string:

logger->info("i has the following value: " + std::to_string(i));

Однако этот баг легко проскальзывает во время разработки. Мой уровень предупреждения установлен на Level4 (/W4).

"i has the following value: " — это c-строка из языка c. Вы не можете использовать + с ним. Или, точнее, + не добавляется.
drescherjm 08.05.2022 22:27

Показанный код не добавляет int ни к одному std::string. Это не то, что делает " .... "+ #. " ...." не std::string, так что это первое. C++ — это не C# и не Java, и он работает совершенно по-другому. Предположение об обратном всегда заканчивается слезами.

Sam Varshavchik 08.05.2022 22:27
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
2
105
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Эта строка правильная,

logger->info("i has the following value: " + i);

в выражении

"i has the following value: " + i

используется арифметика указателя.

Например, если вы напишете

logger->info("i has the following value: " + 6);

то эта строка имеет тот же эффект, если написать

logger->info("the following value: ");

Это эта линия

logger->info("i has the following value: " + i);

эквивалентна строке

logger->info( &"i has the following value: "[i]);

What does the C++ standard say about appending ints to a std::string like I'm doing here

В выражении нет объекта типа std::string. Используется строковый литерал, который имеет обычный тип массива, который является операндом выражения с арифметикой указателя. В выражении строковый литерал неявно преобразуется в указатель на его первый элемент типа const char *.

Ответ принят как подходящий

Проблема в том, что в

logger->info("i has the following value: " + i);

вы не работаете с std::string. Вы добавляете int к строковому литералу, то есть к массиву const char[]. В определенных контекстах const char[] превращается в указатель const char*. В этом случае int продвигает этот указатель вперед на 1337 символов, что намного превышает конец строкового литерала и, следовательно, поведение undefined.

Вы должны получить лучший компилятор, который предупреждает вас об этом, т.е.:

foo.cc:7:42: warning: offset ‘1337’ outside bounds of constant string [-Warray-bounds]
    7 |     foo("i has the following value: " + i);
      |                                          ^

Вы можете использовать литерал std::string следующим образом:

#include <string>
using namespace std::literals;

void foo(std::string);

void bla() {
    int i = 1337;
    foo("i has the following value: "s + i);
}

а затем вы получаете «более приятную» ошибку, что «std::string + int» не существует в C++:

foo.cc:8:40: error: no match for ‘operator+’ (operand types are ‘std::__cxx11::basic_string<char>’ and ‘int’)
    8 |     foo("i has the following value: "s + i);
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^ ~
      |         |                                |
      |         std::__cxx11::basic_string<char> int
...
going on for 147 lines

После этого должно быть очевидно, что вместо этого вы хотите:

logger->info("i has the following value: "s + std::to_string(i));

Использование литералов std::string позволяет избежать подобных ошибок, потому что превращает предупреждения (которые ваш компилятор даже не выдает) в серьезные ошибки, заставляя вас писать правильный код. Поэтому я рекомендую использовать суффикс s для всех строк.

Отличный ответ. You should get a better compiler: Ха-ха, это последний компилятор MSVC, и мне в основном нужно использовать инструменты разработки Windows, так как мое программное обеспечение — только Windows. Хорошо, что хотя бы у gcc есть такое предупреждение. Надеюсь, у MSVC он тоже скоро появится, так как это может расстраивать.

BullyWiiPlaza 08.05.2022 22:49

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

Nathan Pierson 08.05.2022 22:52

Вы можете без особого труда использовать clang и gcc в Windows. Даже если вы предпочитаете использовать msvc для создания конечного продукта, полезно запустить свой код через второй компилятор, чтобы получить другое мнение.

Taekahn 08.05.2022 22:56
You should get a better compiler Это не сильно поможет. Компилятор [сможет] генерировать это предупреждение только тогда, когда он знает (во время компиляции, естественно), что индекс выходит за пределы, и, чаще всего, он не будет знать. И в любом случае, даже если индекс находится в допустимых пределах, вы не получите предупреждения, но код все равно не будет делать то, что вы хотели.
Paul Sanders 08.05.2022 23:20

@PaulSanders Это была копия его жалобы, компилятор не предупредил его: Почему компилятор не предупреждает меня...

Goswin von Brederlow 08.05.2022 23:22

В таком случае, я думаю, вы промахнулись.

Paul Sanders 08.05.2022 23:24

@RemyLebeau Это уже было сказано в вопросе. Смысл суффикса s заключается в защите от случаев, когда вы забываете преобразовать что-то в std::string перед добавлением к нему (или перед передачей в функцию).

Goswin von Brederlow 09.05.2022 05:09

Другие вопросы по теме