C++ 字符连接与 std::string 行为。请解释一下

Posted

技术标签:

【中文标题】C++ 字符连接与 std::string 行为。请解释一下【英文标题】:C++ character concatenation with std::string behavior. Please explain this 【发布时间】:2016-07-25 16:59:37 【问题描述】:

这里有一些我无法理解的关于 c++ std::string 的案例。

1.

string ans = ""+'a';
cout << ans << endl; //Prints _string::copy

2.

string ans="";
ans=ans+'a';
cout << ans << endl; //Prints a

3.

string ans="";
ans = ans + (5 + '0'); // Throws error

4.

string ans="";
ans += (5 + '0'); //works

5.

在代码中,我有一行 ans += to_string(q); q 是一位整数。程序抛出运行时错误。

将其更改为ans+= (q+'0');,错误已被删除。

请帮助清除这个想法。

【问题讨论】:

""+'a' 将超出范围并调用未定义的行为 如果q 是整数,ans += to_string(q); 应该可以正常工作。 如何超出范围?当 + 未重载时,向字符串添加字符是否有效? "" 不是std::string,它是char 的数组,当您向其中添加整数类型时,您将对该数组的地址执行指针运算。这与串联非常不同。 你和其他人都不能重载两边都是基本类型的运算符。所以,"" 是(衰减为)char const *`,'a'char,这意味着您要将字符a 的ASCII 编号添加到该特定空字符串的地址...因此越界并调用UB。但是,如果您想要您错误预期的 std::string 文字,请执行 using namespace std::string_literals; auto wow = "a std::string"s 【参考方案1】:
string ans = ""+'a';

"" 是空字符串文字的地址。 'a' 被解释为整数,ASCII 码 65。这会将 65 添加到文字字符串的地址,这会导致未定义的行为,可能会导致崩溃。

ans=ans+'a';

ans 是一个std::stringstd::string 定义了一个重载的 + 运算符。实际上有几个。其中之一,特别是重载+,其中参数是一个字符,并将该字符附加到字符串中。

ans = ans + (5 + '0'); // Throws error

5+'0' 是提升为int 类型的表达式。 std::string 不会明确地将 + 运算符重载,并将 int 作为参数。这会导致编译错误。

ans += (5 + '0'); //works

std::string 确实有一个明确的重载 += 运算符,所以编译得很好。

【讨论】:

在第 5 种情况下,5+'0' 也被提升为 int。那么,它与第 4 种情况有何不同? 不同的运算符。 ++=,定义不同的重载。 好的,所以没有为 int 定义 +,而为 int 定义了 +=。知道了。谢谢。 不,+= 没有为 int 定义。它仅针对char 定义,但对于operator += 没有其他可以将int 参数转换为的重载,因此没有歧义。 C++11 为使用移动语义的std::string 的右值引用添加了operator+。带有intoperator+ 可以转换为采用char 参数的基于常规复制的operator+,或者采用char 参数的基于移动的operator+,因此会产生歧义。显然,operator+= 没有移动语义。【参考方案2】:

这个:

std::string ans = ""+'a';

不是你想的那样,你实际上执行的操作如下:

const char* p = "";
p = p + 97 /*97=='a'*/; // increase p pointer by `a` value, results in UB (pointer to random memory)
std::string ans = p; // p points to possibly unallocated memory (UB).

这没什么意义。

如果你用 clang 编译它,你会得到很长的警告列表:

main.cpp:22:25: warning: adding 'char' to a string does not append to the string [-Wstring-plus-int]
    std::string ans = ""+'a';
                      ~~^~~~
main.cpp:22:25: note: use array indexing to silence this warning
    std::string ans = ""+'a';
                        ^
                      & [   ]
main.cpp:22:25: warning: adding 'char' to a string pointer does not append to the string [-Wstring-plus-char]
    std::string ans = ""+'a';
                      ~~^~~~
main.cpp:22:25: note: use array indexing to silence this warning
    std::string ans = ""+'a';
                        ^
                      & [   ]

【讨论】:

【参考方案3】:

字符串字面量是一个字符数组。它不是std::string 的实例。数组不能按值传递给函数或运算符,而是在使用操作数时衰减为指向第一个字符的指针。

字符是编码符号的数字。所有字符都有一个非零值,'\0' 除外。

在表达式""+'a' 中,字符串文字衰减为指针,然后'a' 字符被解释为非零整数。该值被添加到指针中。不管a的值是多少(在常用的ASCII编码中恰好是65),结果都超出了数组的范围。超出范围的指针算术具有未定义的行为,输出 1. 是未定义行为的结果。


程序 2. 具有明确定义和预期的行为。


ans = ans + (5 + '0'); // Throws error

没有接受参数std::stringintoperator+。右边的参数是int,因为5 + '0' 中的char 参数被提升为int,因此两个参数属于同一类型。这也是表达式的返回类型。

这是它变得毛茸茸的地方。有一个operator+ 接受char 并且int 可以转换为char。但是,还有其他可能的转换是模棱两可的。这是clang显示的错误:

error: invalid operands to binary expression ('string' (aka 'basic_string<char>') and 'int')

    ans = ans + (5 + '0'); // Throws error

          ~~~ ^ ~~~~~~~~~

./include/c++/6.1.0/bits/basic_string.h:4982:5: note: candidate template ignored: deduced conflicting types for parameter '_CharT' ('char' vs. 'int')

    operator+(const basic_string<_CharT, _Traits, _Alloc>& __lhs, _CharT __rhs)

    ^

./include/c++/6.1.0/bits/basic_string.h:5036:5: note: candidate template ignored: deduced conflicting types for parameter '_CharT' ('char' vs. 'int')

    operator+(basic_string<_CharT, _Traits, _Alloc>&& __lhs,
^

...以及许多其他潜在的重载。


ans += (5 + '0'); //works

这是因为operator+=(char); 是一个明确的重载。


在代码中,我有一行 ans += to_string(q); q 是一位整数。程序抛出运行时错误。

Works fine here, no errors thrown.

【讨论】:

数组不能传递给函数或操作符 那么它们可以通过引用传递, @NathanOliver 资格已添加。

以上是关于C++ 字符连接与 std::string 行为。请解释一下的主要内容,如果未能解决你的问题,请参考以下文章

GCC 如何连接多个 C++ std::string 变量?

奇怪的 C++ std::string 行为......我该如何解决这个问题?

C++ std::string::size()函数(返回字符串的长度,以字节为单位)(与std::string::length()函数相同)

优化字符串的使用:案例研究

std::string 与字节缓冲区(c++ 中的差异)

c# string与c++ std::string的互相转换