Concatenar dos cadenas literales

121

Estoy leyendo Accelerated C ++ de Koenig. Escribe que "la nueva idea es que podemos usar + para concatenar una cadena y una cadena literal - o, para el caso, dos cadenas (pero no dos cadenas literales).

Bien, supongo que esto tiene sentido. Ahora, en dos ejercicios separados destinados a aclarar esto.

¿Son válidas las siguientes definiciones?

const string hello = "Hello";

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

Ahora, intenté ejecutar lo anterior y ¡funcionó! Entonces estaba feliz.

Luego intenté hacer el siguiente ejercicio;

const string exclam = "!";

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

Esto no funcionó. Ahora entiendo que tiene algo que ver con el hecho de que no se pueden concatenar dos cadenas literales, pero no entiendo la diferencia semántica entre por qué logré que el primer ejemplo funcionara (¡no es ", mundo" y "! "¿Dos literales de cadena? ¿No debería haber funcionado esto?) pero no el segundo.

Arthur Collé
fuente
4
const string message = "Hello" ",world" + exclam(por ejemplo, omitiendo el primero +) debería funcionar bien.
2011
1
@Joe - ¿Por qué alguien escribiría "Hello" + ", world!"cuando tú puedes hacerlo "Hello, world!"? Como de costumbre, C ++ tiene una solución sencilla e impresionante para un problema percibido. :-)
Bo Persson
2
@Bo Lo único que puedo pensar es si usas una definición (#define)
Joe Phillips
@Joe Incluso entonces, es más probable que escribas "Hello" ", world!"(sin +). Hay una serie de quejas que uno podría hacer sobre C ++, pero no creo que su manejo aquí sea una de ellas. Es exactamente lo mismo que si escribiera 1 / 3 + 1.5y se quejara porque la división era una división integral. Para bien o para mal, esta es la forma en que funcionan la mayoría de los idiomas.
James Kanze
2
@Bo Persson En realidad, esta función "hello" " world" == "hello world"es útil si tiene que escribir una cadena larga y no quiere que se salga de su ventana o si quiere estar dentro de alguna restricción de longitud de línea. O si una de las cadenas está definida en una macro.
Dimitar Slavchev

Respuestas:

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

El +operador tiene asociatividad de izquierda a derecha, por lo que la expresión entre paréntesis equivalente es:

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

Como puede ver, los dos literales de cadena "Hello"y ",world"se "agregan" primero, de ahí el error.

Una de las dos primeras cadenas que se concatenan debe ser un std::stringobjeto:

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

Alternativamente, puede forzar que el segundo +sea ​​evaluado primero entre paréntesis esa parte de la expresión:

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

Tiene sentido que su primer ejemplo ( hello + ",world" + "!") funcione porque std::string( hello) es uno de los argumentos del extremo izquierdo +. Eso +se evalúa, el resultado es un std::stringobjeto con la cadena concatenada, y ese resultado std::stringluego se concatena con "!".


En cuanto a por qué no puede concatenar dos literales de cadena usando +, es porque un literal de cadena es solo una matriz de caracteres ( const char [N]donde Nes la longitud de la cadena más uno, para el terminador nulo). Cuando usa una matriz en la mayoría de los contextos, se convierte en un puntero a su elemento inicial.

Entonces, cuando intentas hacer "Hello" + ",world", lo que realmente estás tratando de hacer es sumar dos const char*s, lo cual no es posible (¿qué significaría sumar dos punteros?) Y si lo fuera, no haría lo que tu quería que hiciera.


Tenga en cuenta que puede concatenar cadenas literales colocándolas una al lado de la otra; por ejemplo, los dos siguientes son equivalentes:

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

Esto es útil si tiene un literal de cadena larga que desea dividir en varias líneas. Sin embargo, tienen que ser cadenas literales: esto no funcionará con const char*punteros o const char[N]matrices.

James McNellis
fuente
3
Además, const string message = "Hello" + (",world"+exclam);también funcionará, debido al paréntesis explícito (¿es esa una palabra?).
Chinmay Kanchi
1
Podría ser aún más completo si señala por qué funciona el primer ejemplo:const string message = ((hello + ",world") + "!");
Mark Ransom
¡Gracias! Sospechaba que tenía algo que ver con la asociatividad de izquierda a derecha, pero no estaba seguro y esta diferencia semántica no tenía mucho sentido para mí. ¡Aprecio la respuesta!
Arthur Collé
2
Mencionaría que la "Hello" ",world"sintaxis es útil no solo para dividir en varias líneas, sino también cuando uno de los literales de cadena es una macro (o incluso ambos). Luego, la concatenación ocurre en tiempo de compilación.
Melebius
8

Siempre debes prestar atención a los tipos .

Aunque todos parecen cadenas "Hello"y ",world"son literales .

Y en tu ejemplo, exclames un std::stringobjeto.

C ++ tiene una sobrecarga de operador que toma un std::stringobjeto y le agrega otra cadena. Cuando concatenas un std::stringobjeto con un literal, hará la conversión apropiada para el literal.

Pero si intenta concatenar dos literales, el compilador no podrá encontrar un operador que tome dos literales.

Yochai Timmer
fuente
Vea std :: operator + que ofrece sobrecargas para concatenar un std::stringcon otro std::string, una matriz de caracteres o un solo carácter.
DavidRR
7

Su segundo ejemplo no funciona porque no hay operator +dos literales de cadena. Tenga en cuenta que un literal de cadena no es de tipo string, sino de tipo const char *. Su segundo ejemplo funcionará si lo revisa así:

const string message = string("Hello") + ",world" + exclam;
Juraj Blaho
fuente
4

Desde C ++ 14, puede usar dos literales de cadena reales :

const string hello = "Hello"s;

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

o

const string exclam = "!"s;

const string message = "Hello"s + ",world"s + exclam;
Thomas Sablik
fuente
2

En el caso 1, debido al orden de las operaciones, obtiene:

(hola + ", mundo") + "!" que se resuelve en hola + "!" y finalmente para saludar

En el caso 2, como señaló James, obtienes:

("Hola" + ", mundo") + exclam que es la concatenación de 2 cadenas literales.

Espero que esté claro :)

Stephen
fuente
1

La diferencia entre una cadena (o para ser precisos std::string) y un carácter literal es que para este último no hay un +operador definido. Por eso falla el segundo ejemplo.

En el primer caso, el compilador puede encontrar un valor adecuado, operator+siendo el primer argumento un stringy el segundo un carácter literal ( const char*), por lo que lo usó. El resultado de esa operación vuelve a ser a string, por lo que repite el mismo truco al agregarle "!".

Péter Török
fuente