连接两个字符串字面值。

133

我正在阅读 Koenig 的《Accelerated C++》。他写道,“新的想法是,我们可以使用+来连接字符串和字符串字面值——或者说,两个字符串(但不能是两个字符串字面值)。”

好的,我想这很有道理。现在我们来看两个旨在阐明这一点的练习。

以下定义是否有效?

const string hello = "Hello";

const string message = hello + ",world" + "!";

现在,我尝试执行上面的内容,并且它成功了!所以我很高兴。

然后我尝试做下一个练习;

const string exclam = "!";

const string message = "Hello" + ",world" + exclam;

这个没有起作用。现在我明白了,这似乎与不能连接两个字符串文本有关,但我不理解为什么我设法使第一个示例起作用(",world"和"!"不是两个字符串文本吗?这不应该起作用吗?),但第二个示例却无法起作用。


4
const string message = "Hello" ",world" + exclam (例如省略第一个+)应该可以正常工作。 翻译:上述代码可以正常运行,将会得到字符串 "Hello, world!",其中的变量 exclam 代表感叹号。 - n0rd
1
@Joe - 为什么有人会写"Hello" + ", world!",当你可以直接写"Hello, world!"。像往常一样,C++ 对于看似的问题有一个很棒且简单的解决方法。 :-) - Bo Persson
2
@Bo 我唯一能想到的是,如果你使用了一个定义 (#define)。 - Joe Phillips
即使这样,你更可能写成"Hello" ", world!"(没有+)。对于C++,我们可以提出很多抱怨,但我不认为它的处理方式是其中之一。这与您编写1/3 + 1.5并抱怨因为除法是整数除法是完全相同的。无论好坏,这是大多数语言的工作方式。 - James Kanze
我不反对。在意识到使用案例的荒谬之前,我写下了那句话。 - Joe Phillips
4
@Bo Persson,实际上这个特性"hello" " world" == "hello world"非常有用,如果您需要编写一个长字符串并且不想让它超出窗口或者想要保持一定的行长度限制,那么就可以使用它。或者如果其中一个字符串是在宏中定义的。 - Dimitar Slavchev
7个回答

155
const string message = "Hello" + ",world" + exclam;

加号(+)运算符是从左往右结合的,因此等价的带括号表达式为:

const string message = (("Hello" + ",world") + exclam);

正如你所看到的,两个字符串字面量"Hello"",world"首先被“相加”,因此出现了错误。

被连接的前两个字符串中必须有一个是std::string对象:

const string message = string("Hello") + ",world" + exclam;

或者,您可以通过给表达式的这一部分加上括号来强制先计算第二个 +

const string message = "Hello" + (",world" + exclam);

第一个例子 (hello + ",world" + "!") 能够工作是因为 std::string (hello) 是最左侧的 + 运算符的一个参数。当该 + 被计算时,结果是一个具有已连接字符串的 std::string 对象,然后将该结果与 "!" 进行拼接。


至于为什么你不能使用 + 来连接两个字符串字面量,这是因为字符串字面量只是一个字符数组(一个 const char [N],其中 N 是字符串长度加一,用于表示空终止符)。在大多数上下文中使用数组时,它会转换为指向其初始元素的指针。

所以,当你尝试执行 "Hello" + ",world" 时,你实际上正在尝试将两个 const char* 相加,这是不可能的(将两个指针相加会意味着什么?)并且即使可以也无法实现你想要的功能。


请注意,你可以通过将它们放在一起来连接字符串字面量;例如,以下两个示例是等效的:

"Hello" ",world"
"Hello,world"

如果你有一个很长的字符串字面量需要分成多行,这个技巧就很有用了。但请注意,这些必须是字符串字面量,不能用于 const char* 指针或 const char[N] 数组。


3
也就是说,const string message = "Hello" + (",world"+exclam); 也是可以正常工作的,因为使用了显式的括号。 - Chinmay Kanchi
2
如果您指出第一个示例为什么有效,那么它可能会更加完整:const string message = ((hello + ",world") + "!"); - Mark Ransom
谢谢!我怀疑这与从左到右的结合性有关,但不确定,而且这种语义差异对我来说没有多大意义。感谢您的回答! - Arthur Collé
4
我提及"Hello", "world"语法不仅有助于将字符串分成多行,而且当其中一个字符串字面量是宏(或者两个字符串字面量都是宏)时也非常有用。然后连接会在编译时发生。 - Melebius
@Melebius 真的!我最近才发现这是可能的,并且现在感到非常愚蠢之前没有学习它 =\ 您可以像这样连接宏字符串:#define (x) #x ## " is a foo." # - "字符串化"值,而 ## 连接 2 个字符串,如 "x"" 是一个 foo。" - ScienceDiscoverer
谢谢,这终于帮助我理解了如何快速组合(连接)字符串,就像在C#中那样。C++似乎更啰嗦一些 //C#: string strA = "User [" + GetUserIdAsInt() + "] has done " + GetActionName(); //C++ std::string strB = std::string("User [" + GetUserIdAsInt()) + "] has done " + GetActionName(); C++包含了比C#多26%的字符数,哈哈。 - Reahreic

10

从C++14开始,您可以使用两个真正的字符串字面量

const string hello = "Hello"s;

const string message = hello + ",world"s + "!"s;

或者

const string exclam = "!"s;

const string message = "Hello"s + ",world"s + exclam;

9

您应该始终注意类型

尽管它们都看起来像字符串,"Hello"",world"字面量

在您的示例中,exclam是一个std::string对象。

C++有一个运算符重载,它接受一个std::string对象并将另一个字符串添加到它上面。当您将std::string对象与字面量连接时,它会为字面量进行适当的转换。

但是,如果您尝试连接两个字面量,编译器将找不到接受两个字面量的运算符。


请参见std::operator+,它提供了用于将std::string与另一个std::string、字符数组或单个字符连接的重载。 - DavidRR

8

你的第二个示例不起作用,因为没有适用于两个字符串字面值的 operator+。请注意,字符串字面值不是 string 类型,而是 const char * 类型。如果你像这样修改第二个示例,则它将起作用:

const string message = string("Hello") + ",world" + exclam;

2
字符串(或者准确地说,std::string)和字符字面量的区别在于,对于后者没有定义+运算符。这就是为什么第二个示例失败的原因。
在第一个示例中,编译器可以找到一个合适的operator+,其第一个参数是一个字符串(string),第二个参数是一个字符字面量(const char*),所以它使用了那个。该操作的结果再次是一个字符串(string),因此当添加"!"时,它重复相同的技巧。

严谨地说,字符字面量确实定义了 +,只是不能使用两个 char* 来进行操作。否则,指针算术运算将无法正常工作 :) - Qix - MONICA WAS MISTREATED

2
在情况1中,由于操作顺序,您会得到:(hello + ", world")+ "!",这将解析为 hello + "!",最终得到 hello。
在情况2中,正如詹姆斯所指出的,您会得到:("Hello" + ", world") + exclam,这是两个字符串文字的连接。
希望清楚明了 :)

0

如果我们写下:

string s = "hello" + "world!";

RHS 的类型如下:

const char [6] + const char [7]

现在两者都是内置数据类型。

也就是说,它们不再是 std::string 类型。

因此,现在编译器定义的内置类型的运算符重载适用。

即 - 不再有由 std::string 重载的 + 运算符。

现在让我们来看看编译器如何重载运算符。

  • 针对两个 const char * 类型的操作数的二元运算符。

事实证明,编译器没有为这种情况重载运算符,因为这是没有意义的。

也就是说,将两个 'const char *' 相加在语义上是错误的,因为结果在运行时仍然会得到另一个 const char *。以上有很多原因不合理。

因此,总的来说,任何运算符重载都有一个通用规则。它是:

当运算符的所有操作数都是内置类型时,才能重载该运算符。编译器设计者将照顾这些情况。在我们的问题中,std::string 不能重载两个 'const literals',因为有这个规则,编译器选择不为其无意义的 + 二元运算符实现重载。

如果我们喜欢字符串字面形式,我们可以使用以下"s"运算符。 std::string p = "hello"s + "world!"s;
只需加上后缀"s",意义就会改变。 (s重载运算符)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接