我注意到,当我初始化一个字符串时,编译器报告了一个我没想到的错误。
例如:
#include <iostream>
#include <string>
using namespace std;
int main() {
string s1 = "Hello", s2 = "World!"; // ok
string s3 = s1 + ", " + "World!"; // ok
string s4 = "Hello" + ", " + s2; // error
cout << s1 + " " + s2 << endl; //ok
return 0;
}
对我来说,如果s3
工作得很好,s4
也应该这样做。
为什么我会收到该错误?这两个初始化字符串(s3
和s4
)之间有什么区别?
答案 0 :(得分:13)
"Hello"
不是std::string
(而是const char[6]
,而", "
是const char[3]
)和+
运营商std::string
不适用。
这对C ++来说是一个小小的不便,源于它的C系列。这意味着在通过+
连接字符串时,必须确保其两个操作数中至少有一个实际上是std::string
,如
auto s = std::string{"Hello"} + ", " + "World";
其中第一个+
有std::string
作为其第一个操作数,因此产生std::string
,因此第二个+
也有一个std::string
它的第一个操作数(从+
开始从左到右处理)。
Edit1 ,我提到如果只用空格分隔,C字符串文字会自动连接:
std::string s = "Hello" ", " "World";
此行为也是从C继承的:预处理器将上面的代码呈现为
std::string s = "Hello, World";
在编译器正确处理它之前(更准确地说,字符串连接发生在转换的phase 6中,就在编译之前)。这实际上是连接原始字符串文字的最简单,也是最方便的方法。但请注意,我必须声明s
的类型,因为auto
扣除会产生const char*
。
Edit2 ,我提到自C ++ 14以来,您只需添加std::string
后即可直接形成s
文字字符串 if 你拉入相关的operator""s
(或周围的命名空间)。
using std::literals::operator""s; // pull in required operator
const auto s = "Hello"s + ", " + "World"; // std::string
请参阅this post,了解嵌套命名空间中隐藏所需operator""s
的原因。另请注意,除using std::literals::operator""s;
之外,您可以拉入周围的namespace
:以下任一声明都可以。
using namespace std::string_literals;
using namespace std::literals::string_literals;
using namespace std::literals;
并不像普通using namespace std;
那样糟糕(和该死)。
答案 1 :(得分:5)
以下行尝试将2个指针添加到一起。
string s4 = "Hello" + ", " + s2; // error
"Hello"
是一个const char[6]
,其衰减为const char*
而", "
是一个变为char[3]
的衰变为const char*
。
您正在尝试为std::string
添加两个指针,这是无效的。此外,添加两个指针也无效。
正如其他人所说,这与operator+
类的std::string
的重载没有任何关系,因为以下代码也不会因为同样的原因而编译:
string s4 = "Hello" + ", ";
我添加了一些用户 @dyp 提供的说明。
您可以使用C ++标准库&#34;用户定义&#34;来解决您的问题。字符串字面量。该功能由std::literals::string_literals::operator""s
提供,可在<string>
标头中找到。您可能必须使用using-directive或-declaration;我的编译器不需要这个,包括<string>
标题就足够了。例如:
string s4 = "Hello"s + ", "s + s2;
这相当于:
string s4 = std::string("Hello") + std::string(", ") + s2;
当您将std::string
的实例作为第一个操作数时,表达式的其余部分将使用该类型,因为operator +的左右关联将确保std::string
的实例是每次都返回最左边的操作数,而不是const char*
。这就是为什么你没有在线错误:
string s3 = s1 + ", " + "World!";
该行相当于:
string s3 = ((s1 + ", ") + "World!");
答案 2 :(得分:5)
在C ++中,字符串通常表示为std::string
或C-string,其类型为char[N]
,其中N
是C字符串中的字符数加上一个用于终止字符(或空字符)\0
。
将字符串文字作为例如,"Hello"
传递给函数时,实际上是在传递char[6]
,衰减指针char*
,因为C ++不允许您按值传递数组。此外,C ++ 不允许非常量指针指向字符串文字,因此参数类型必须变为const char*
。
std::string
具有binary addition operator的现有重载,允许您将两个std::string
对象连接成一个,以及将std::string
对象与C字符串连接起来。
std::string s1 = "Hello";
std::string s2 = "World";
// Uses 'std::string operator+(const std::string&, const std::string&)'.
std::string s3 = s1 + s2;
// Uses 'std::string operator+(const std::string&, const char*)'.
std::string s4 = s1 + "World";
// Also ok.
std::string s4 = "Hello" + s2;
但是,连接两个C字符串没有重载。
// Error. No overload for 'std::string operator+(const char*, const char*)' found.
std::string s5 = "Hello" + "World";
在C ++中执行添加left to right,因此,长连接表达式需要以std::string
对象开头。
std::string s6 = "Hello" + ", " + s2; // Error. Addition is performed left-to-right and
// you can't concatenate two C-strings.
std::string s7 = s1 + ", " + "World"; // Ok. 's1 + ", "' returns a new temporary
// 'std::string' object that "World" is concat'ed to.
使用C ++ 14 标准文字,您可以从文字表达式创建std::string
个对象(请注意文字表达式中的尾随s
)。
using namespace std::literals;
std::string s8 = "Hello"s + ", " + "World"; // Ok
或者,您可以使用预处理器将自动连接用空格分隔的文字字符串这一事实。
std::string s9 = "Hello" ", " "World"; // Ok
但是,你可以完全跳过这个空格并使用单个字符串文字。
答案 3 :(得分:3)
首先要理解的是,由于C语言中的祖先,C ++中的字符串文字如"Hello"
而不是实际上是string
类型。它的类型实际上是一个字符数组。
因此,s3
和s4
之间的区别在于,当您执行s3
构造时,完成的第一个操作是s1 + ", "
。 s1
的类型为字符串,因此使用标准库的字符串组件提供的+
运算符,并返回要添加到"World!"
的新字符串。
现在,对于s4
,要执行的第一个操作是"Hello" + ", "
或添加两个字符串文字。这个操作实际上会被编译器视为将两个字符数组一起添加,这种语言不能提供这种功能,从而导致您看到的错误。
最简单的解决方法是不要将+
用于字符串文字,只需将它们放在同一个字符串中即可。或者,您可以从第一个文字创建string
并让操作从那里开始工作(std::string("Hello") + ", " + s2
)
答案 4 :(得分:2)
+运算符在std :: string中重载。
运算符重载应用于操作数,因此
s1 + ...
通过调用s1上的重载+运算符来工作。
文字"Hello"
是const char *
,","
也是const char *
。 string("Hello") + ...
没有重载+运算符,因此出错。
试试fixed property.
即可。
感谢@interjay的修正