Concaténer deux chaînes littérales

121

Je lis Accelerated C ++ par Koenig. Il écrit que «la nouvelle idée est que nous pouvons utiliser + pour concaténer une chaîne et une chaîne littérale - ou, d'ailleurs, deux chaînes (mais pas deux chaînes littérales).

Bien, cela a du sens, je suppose. Passons maintenant à deux exercices distincts destinés à éclairer cela.

Les définitions suivantes sont-elles valides?

const string hello = "Hello";

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

Maintenant, j'ai essayé d'exécuter ce qui précède et cela a fonctionné! Alors j'étais content.

Ensuite, j'ai essayé de faire l'exercice suivant;

const string exclam = "!";

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

Cela n'a pas fonctionné. Maintenant, je comprends que cela a quelque chose à voir avec le fait que vous ne pouvez pas concaténer deux chaînes littérales, mais je ne comprends pas la différence sémantique entre pourquoi j'ai réussi à faire fonctionner le premier exemple (n'est pas ", world" et "! "deux chaînes littérales? Cela n'aurait-il pas dû fonctionner?) mais pas le second.

Arthur Collé
la source
4
const string message = "Hello" ",world" + exclam(par exemple en omettant le premier +) devrait fonctionner très bien.
n0rd
1
@Joe - Pourquoi quelqu'un écrirait-il "Hello" + ", world!"alors que vous pouvez le faire "Hello, world!". Comme d'habitude, C ++ a une solution de contournement impressionnante et simple pour un problème perçu. :-)
Bo Persson
2
@Bo La seule chose à laquelle je pense, c'est si vous utilisez une définition (#define)
Joe Phillips
@Joe Même dans ce cas, vous êtes plus susceptible d'écrire "Hello" ", world!"(sans le +). Il y a un certain nombre de plaintes que l'on pourrait faire à propos du C ++, mais je ne pense pas que sa gestion ici en soit une. C'est exactement la même chose que si vous écriviez 1 / 3 + 1.5et que vous vous plaigniez parce que la division était une division intégrale. Pour le meilleur ou pour le pire, c'est ainsi que fonctionnent la plupart des langues.
James Kanze
2
@Bo Persson En fait, cette fonctionnalité "hello" " world" == "hello world"est utile si vous devez écrire une longue chaîne et que vous ne voulez pas qu'elle sorte de votre fenêtre ou que vous voulez être dans une certaine restriction de longueur de ligne. Ou si l'une des chaînes est définie dans une macro.
Dimitar Slavchev

Réponses:

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

L' +opérateur a une associativité de gauche à droite, donc l'expression équivalente entre parenthèses est:

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

Comme vous pouvez le voir, les deux chaînes littérales "Hello"et ",world"sont «ajoutées» en premier, d'où l'erreur.

L'une des deux premières chaînes concaténées doit être un std::stringobjet:

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

Vous pouvez également forcer le second +à être évalué en premier en mettant entre parenthèses cette partie de l'expression:

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

Il est logique que votre premier exemple ( hello + ",world" + "!") fonctionne car le std::string( hello) est l'un des arguments à l'extrême gauche +. Cela +est évalué, le résultat est un std::stringobjet avec la chaîne concaténée, et ce résultat std::stringest ensuite concaténé avec le "!".


Quant à savoir pourquoi vous ne pouvez pas concaténer deux chaînes littérales en utilisant +, c'est parce qu'une chaîne littérale est juste un tableau de caractères ( const char [N]N est la longueur de la chaîne plus un, pour le terminateur nul). Lorsque vous utilisez un tableau dans la plupart des contextes, il est converti en un pointeur vers son élément initial.

Donc, quand vous essayez de faire "Hello" + ",world", ce que vous essayez vraiment de faire est d'ajouter deux const char*s ensemble, ce qui n'est pas possible (qu'est-ce que cela signifierait d'ajouter deux pointeurs ensemble?) Et si c'était le cas, cela ne ferait pas ce que vous voulait qu'il le fasse.


Notez que vous pouvez concaténer des chaînes littérales en les plaçant les unes à côté des autres; par exemple, les deux suivants sont équivalents:

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

Ceci est utile si vous avez une longue chaîne littérale que vous souhaitez diviser en plusieurs lignes. Ils doivent cependant être des chaînes littérales: cela ne fonctionnera pas avec des const char*pointeurs ou des const char[N]tableaux.

James McNellis
la source
3
De plus, const string message = "Hello" + (",world"+exclam);cela fonctionnera aussi, à cause de la parenthèse explicite (est-ce un mot?).
Chinmay Kanchi
1
Pourrait être encore plus complet si vous indiquez pourquoi le premier exemple fonctionne:const string message = ((hello + ",world") + "!");
Mark Ransom
Je vous remercie! Je soupçonnais que cela avait quelque chose à voir avec l'associativité de gauche à droite, mais je n'étais pas sûr et cette différence sémantique n'avait pas beaucoup de sens pour moi. J'apprécie la réponse!
Arthur Collé
2
Je mentionnerais que la "Hello" ",world"syntaxe est utile non seulement pour diviser en plusieurs lignes, mais également lorsque l'un des littéraux de chaîne est une macro (ou même les deux). Ensuite, la concaténation se produit au moment de la compilation.
Melebius
8

Vous devez toujours faire attention aux types .

Bien qu'ils ressemblent tous à des chaînes, "Hello"et qu'ils ",world"soient littéraux .

Et dans votre exemple, exclamest un std::stringobjet.

C ++ a une surcharge d'opérateur qui prend un std::stringobjet et y ajoute une autre chaîne. Lorsque vous concaténez un std::stringobjet avec un littéral, il effectuera le cast approprié pour le littéral.

Mais si vous essayez de concaténer deux littéraux, le compilateur ne pourra pas trouver un opérateur qui prend deux littéraux.

Yochai Timmer
la source
Voir std :: operator + qui offre des surcharges pour concaténer un std::stringavec un autre std::string, un tableau de caractères ou un seul caractère.
DavidRR
7

Votre deuxième exemple ne fonctionne pas car il n'y a pas operator +pour deux chaînes littérales. Notez qu'un littéral de chaîne n'est pas de type string, mais plutôt de type const char *. Votre deuxième exemple fonctionnera si vous le révisez comme ceci:

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

Depuis C ++ 14, vous pouvez utiliser deux littéraux de chaîne réels :

const string hello = "Hello"s;

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

ou

const string exclam = "!"s;

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

Dans le cas 1, en raison de l'ordre des opérations, vous obtenez:

(bonjour + ", monde") + "!" qui se résout en bonjour + "!" et enfin bonjour

Dans le cas 2, comme James l'a noté, vous obtenez:

("Hello" + ", world") + exclam qui est le concat de 2 chaînes littérales.

J'espère que c'est clair :)

Stephen
la source
1

La différence entre une chaîne (ou pour être précis std::string) et un littéral de caractère est que pour ce dernier, aucun +opérateur n'est défini. C'est pourquoi le deuxième exemple échoue.

Dans le premier cas, le compilateur peut trouver un convenable operator+avec le premier argument étant a stringet le second un caractère littéral ( const char*) donc il l'a utilisé. Le résultat de cette opération est à nouveau un string, donc il répète la même astuce lors de l'ajout "!".

Péter Török
la source