В своем коде я использую протоколирование заявлений, чтобы лучше видеть, что происходит. Иногда я пишу код, подобный следующему:
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)
.
Показанный код не добавляет int ни к одному std::string
. Это не то, что делает " .... "+ #
. " ...."
не std::string
, так что это первое. C++ — это не C# и не Java, и он работает совершенно по-другому. Предположение об обратном всегда заканчивается слезами.
Эта строка правильная,
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 он тоже скоро появится, так как это может расстраивать.
MSVC, я думаю, имеет предупреждение об этом в своем статический анализ кода. Кажется, было бы неплохо включить это автоматически.
Вы можете без особого труда использовать clang и gcc в Windows. Даже если вы предпочитаете использовать msvc для создания конечного продукта, полезно запустить свой код через второй компилятор, чтобы получить другое мнение.
You should get a better compiler
Это не сильно поможет. Компилятор [сможет] генерировать это предупреждение только тогда, когда он знает (во время компиляции, естественно), что индекс выходит за пределы, и, чаще всего, он не будет знать. И в любом случае, даже если индекс находится в допустимых пределах, вы не получите предупреждения, но код все равно не будет делать то, что вы хотели.
@PaulSanders Это была копия его жалобы, компилятор не предупредил его: Почему компилятор не предупреждает меня...
В таком случае, я думаю, вы промахнулись.
@RemyLebeau Это уже было сказано в вопросе. Смысл суффикса s
заключается в защите от случаев, когда вы забываете преобразовать что-то в std::string
перед добавлением к нему (или перед передачей в функцию).
"i has the following value: "
— это c-строка из языкаc
. Вы не можете использовать+
с ним. Или, точнее,+
не добавляется.