web-dev-qa-db-fra.com

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

La rubrique des chaînes dans la documentation SO utilisée 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" à std::string"foo".

Le seul avantage que je vois en utilisant

std::string str = "foo"s;

au lieu de

std::string str = "foo";

est que dans le premier cas le compilateur peut effectuer la copie-élision (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 aussi appeler un constructeur, le constructeur de copie.

Ignorer les cas où il est obligatoire pour utiliser std::string littéraux comme

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

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

57
Rakete1111

Si vous faites partie de la foule "Presque toujours automatique", alors l'UDL est très important. Il vous permet de faire ceci:

auto str = "Foo"s;

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

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

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

Ou toute forme de déduction de type, vraiment:

template<typename T>
void foo(T &&t) {...}

foo("Foo"s);

Le seul avantage que je vois en utilisant [...] au lieu de [...] est que dans le premier cas le compilateur peut effectuer la copie-élision (je pense), ce qui serait plus rapide que l'appel du constructeur dans le second cas.

La copie-élision n'est pas plus rapide que l'appel du constructeur. Dans tous les cas, vous appelez l'un des constructeurs de l'objet. La question est laquelle:

std::string str = "foo";

Cela provoquera un appel au constructeur de std::string qui prend un const char*. Mais depuis std::string doit copier la chaîne dans son propre stockage, il doit obtenir la longueur de la chaîne pour le faire. Et comme il ne connaît pas la longueur, ce constructeur est obligé d'utiliser strlen pour l'obtenir (techniquement, char_traits<char>::length, mais ce ne sera probablement pas beaucoup plus rapide).

Par contre:

std::string str = "foo"s;

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

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

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

Le conseil en question n'est pas pour vous de faire le tour et de convertir chaque utilisation d'un littéral dans la version s. Si vous êtes d'accord avec les limitations d'un tableau de chars, utilisez-le. Le conseil est que, si vous stockez ce littéral dans un std::string, il est préférable de le faire pendant qu'il est encore littéral et non nébuleux const char*.

49
Nicol Bolas

Les conseils d'utilisation "blah"s n'a rien à voir avec l'efficacité et tout à voir avec l'exactitude du code novice.

Les novices C++ qui n'ont pas d'expérience en C ont tendance à supposer que "blah" résulte en un objet d'un type de chaîne raisonnable. Par exemple, pour que l'on puisse écrire des choses comme "blah" + 42, qui fonctionne dans de nombreux langages de script. Avec "blah" + 42 en C++, cependant, on ne fait qu'encourir un comportement indéfini, s'adressant au-delà de la fin du tableau de caractères.

Mais si ce littéral de chaîne est écrit comme "blah"s alors on obtient à la place une erreur de compilation, ce qui est bien préférable.

19

De plus, UDL permet d'avoir plus facilement \0 dans la chaîne

std::string s = "foo\0bar"s; // s contains a \0 in its middle.
std::string s2 = "foo\0bar"; // equivalent to "foo"s
13
Jarod42
  1. L'utilisation d'un littéral de chaîne C++ signifie que nous n'avons pas besoin d'appeler strlen pour calculer la longueur. Le compilateur le sait déjà.
  2. Pourrait permettre des implémentations de bibliothèque où les données de chaîne pointent vers la mémoire dans l'espace global. L'utilisation des littéraux C doit toujours forcer une copie des données à stocker de la mémoire lors de la construction.
2
doron