为什么在 C++ 中没有编译器警告的情况下将 int 附加到 std::string 未定义行为?

Why is appending an int to a std::string undefined behavior with no compiler warning in C++?

在我的代码中,我使用 logging statements 以便更好地了解发生了什么。有时我会写如下代码:

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).

这一行是正确的,

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 个字符,这远远超出了字符串文字的末尾,因此是未定义的行为。

你应该得到一个更好的编译器来警告你,即:

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 后缀。