69 votes

Avantages de l'utilisation de littéral utilisateur défini pour les chaînes au lieu du littéral de chaîne

Le sujet des chaînes dans la documentation SO utilisé pour dire, dans la section Remarques:

Depuis C++14, au lieu d'utiliser "foo", il est recommandé d'utiliser "foo"s, car s est un littéral de chaîne, qui convertit le const char * "foo" en std::string "foo".

Le seul avantage que je vois en utilisant

std::string str = "foo"s;

au lieu de

std::string str = "foo";

est qu'avec le premier cas le compilateur peut réaliser une élision de copie (je pense), ce qui serait plus rapide que l'appel du constructeur dans le second cas.

Néanmoins, cela n'est (pas encore) garanti, donc le premier pourrait également appeler un constructeur, le constructeur de copie.

En ignorant les cas où il est nécessaire d'utiliser des littéraux de std::string comme

std::string str = "Hello "s + "World!"s;

Y a-t-il un avantage à utiliser des std::string littéraux au lieu de littéraux const char[]?

3 votes

Errr... Est-ce que la déduction de type auto compte ? Le conseil presque toujours auto suscite en effet quelque controverse.

1 votes

Beaucoup de choses en C++ sont liées à la sémantique. L'idéal est de décrire aussi précisément que possible ce que vous voulez faire, et de laisser le compilateur gérer tout le reste. Cependant, ne pas en faire trop pour laisser le compilateur avoir de la marge de manœuvre (et optimiser).

3 votes

Considérez le cas où vous passez une chaîne littérale en tant que paramètre avec un type qui est constructible à partir de std::string, mais pas à partir d'une chaîne C.

66voto

Nicol Bolas Points 133791

Si vous faites partie du groupe "Presque Toujours Auto", alors l'UDL est très importante. Elle vous permet de faire ceci :

auto str = "Foo"s;

Et ainsi, str sera un véritable std::string, et non un const char*. Cela vous permet donc de décider quand faire quoi.

C'est également important pour la déduction du type de retour automatique :

[]() {return "Foo"s;}

Ou toute forme de déduction de type, en réalité :

template
void foo(T &&t) {...}

foo("Foo"s);

Le seul avantage que je vois à utiliser [...] au lieu de [...] est qu'avec le premier cas, le compilateur peut effectuer une élision de copie (je pense), ce qui serait plus rapide que l'appel au constructeur dans le second cas.

L'élision de copie n'est pas plus rapide que l'appel au constructeur. De toute façon, vous appelez un des constructeurs de l'objet. La question est lequel :

std::string str = "foo";

Cela provoquera un appel au constructeur de std::string qui prend un const char*. Mais puisque std::string doit copier la chaîne dans son propre espace de stockage, elle doit obtenir la longueur de la chaîne pour le faire. Et puisqu'elle ne connaît pas la longueur, ce constructeur doit utiliser strlen pour l'obtenir (techniquement, char_traits::length, mais ce n'est probablement pas beaucoup plus rapide).

En revanche :

std::string str = "foo"s;

Cela utilisera le modèle UDL qui a ce prototype :

string operator "" s(const char* str, size_t len);

Voyez-vous, le compilateur connaît la longueur d'une chaîne littérale. Ainsi, le code UDL reçoit un pointeur vers la chaîne et une taille. Et donc, il peut appeler le constructeur de std::string qui prend un const char* et un size_t. Ainsi, il n'est pas nécessaire de calculer la longueur de la chaîne.

Le conseil en question n'est pas que vous convertissiez chaque utilisation d'une littérale en la version s. Si vous êtes bien avec les limitations d'un tableau de chars, utilisez-le. Le conseil est que, si vous allez stocker cette littérale dans un std::string, il est préférable de le faire pendant qu'il est encore une littérale et non un const char* nébuleux.

2 votes

La déduction du type de fonction du modèle pourrait également être un bon exemple.

0 votes

1 Il n'y a aucun problème à faire auto str = "Foo";, donc l'affirmation selon laquelle "Foo"s vous permet de le faire est tout simplement absurde.

4 votes

@Cheersandhth.-Alf: Si vous voulez une std::string plutôt qu'un tableau, alors faire auto str = "Foo"; pose problème.

24voto

Le conseil d'utiliser "blah"s n'a rien à voir avec l'efficacité et tout à voir avec la correction pour le code novice.

Les novices en C++ qui n'ont pas de formation en C, ont tendance à supposer que "blah" résulte en un objet d'un type de chaîne de caractères raisonnable. Par exemple, de sorte que l'on puisse écrire des choses comme "blah" + 42, ce qui fonctionne dans de nombreux langages de script. Avec "blah" + 42 en C++, cependant, on se retrouve simplement avec un Comportement Indéfini, pointant au-delà de la fin du tableau de caractères.

Mais si cette littérale de chaîne est écrite comme "blah"s alors on obtient à la place une erreur de compilation, ce qui est beaucoup plus préférable.

22voto

Jarod42 Points 15729

De plus, l'UDL facilite l'inclusion de \0 dans la chaîne

std::string s = "foo\0bar"s; // s contient un \0 au milieu.
std::string s2 = "foo\0bar"; // équivalent à "foo"s

0 votes

En ce qui concerne le lien vers l'autorité présumée (puisque des informations plus détaillées ne sont plus disponibles) : la documentation SO n'est pas une autorité, c'est tout le contraire : totalement non fiable. Tel est le cas depuis la fin juillet 2016. cppreference.com est une bonne ressource vers laquelle faire un lien.

2voto

doron Points 10296
  1. L'utilisation d'une chaîne de caractères C++ signifie que nous n'avons pas besoin d'appeler strlen pour calculer la longueur. Le compilateur le sait déjà.
  2. Peut permettre des implémentations de bibliothèque où les données de la chaîne pointent vers la mémoire de l'espace global tandis que l'utilisation de littéraux C oblige toujours à copier les données dans la mémoire heap lors de la construction.

2 votes

Les chaînes COW ne sont plus autorisées (depuis C++11), donc je ne peux pas imaginer comment #2 serait possible. Si std :: string contient une chaîne, elle doit en être propriétaire et le faire de manière unique.

0 votes

Il ne s'agit pas de VACHE, juste d'utiliser l'espace global comme espace de stockage initial.

2 votes

Si vous utilisez un espace global comme stockage initial, il est très possible que plus d'une `std::string` utilise le même stockage global. Dans presque tous les compilateurs, si vous utilisez le même littéral deux fois, il ne sera présent dans la table des chaînes qu'une seule fois. Ainsi, si vous essayez de modifier cette `string`, l'objet devra d'abord la copier dans un stockage spécifique à l'objet. C'est l'essence même de COW.

2voto

Costantino Grana Points 1014

Ceci est ancien et a déjà des réponses merveilleuses. Je veux juste ajouter un autre cas d'utilisation pour l'UDL pour std::string:

for (char cur : "abcdefghijklR") {
    // cela bouclera de 'a' à 'R' et ajoutera une autre boucle avec cur=0
}

for (char cur : "abcdefghijklR"s) {
    // cela bouclera de 'a' à 'R'
}

Juste mes deux cents.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X