Considérez cet extrait de code:
bool foo(const std::string& s) {
return s == "hello"; // comparing against a const char* literal
}
bool bar(const std::string& s) {
return s == "hello"s; // comparing against a std::string literal
}
À première vue , il semble que comparer avec un const char*
Nécessite moins d'instructions d'assemblage1, car l'utilisation d'un littéral de chaîne entraînera une construction sur place du std::string
.
( EDIT: Comme indiqué dans les réponses, j'ai oublié le fait que s.compare(const char*)
sera effectivement appelé dans foo()
, donc bien sûr, aucune construction sur place n'a lieu dans ce cas. Par conséquent, supprimez certaines lignes ci-dessous. )
Cependant, en regardant la référence operator==(const char*, const std::string&)
:
Toutes les comparaisons sont effectuées via la fonction membrecompare()
.
D'après ma compréhension, cela signifie que nous devrons de toute façon construire un std::string
Afin d'effectuer la comparaison, donc je soupçonne que les frais généraux seront les mêmes à la fin (bien que masqués par l'appel à operator==
).
1 Je suis conscient que moins d'instructions d'assemblage ne signifie pas nécessairement un code plus rapide, mais je ne veux pas entrer ici dans le micro-benchmarking.
Si vous voulez être intelligent, comparez à "string"sv
, qui renvoie un std::string_view
.
En comparant un littéral comme "string"
n'entraîne aucune surcharge d'allocation, il est traité comme une chaîne terminée par null, avec tous les inconvénients concomittants: aucune tolérance pour les nulls incorporés, et les utilisateurs doivent tenir compte du terminateur null.
"string"s
fait une allocation, sauf optimisation des petites chaînes ou élision d'allocation . De plus, l'opérateur obtient la longueur du littéral, pas besoin de compter, et il permet des valeurs NULL incorporées.
Et enfin en utilisant "string"sv
combine les avantages des deux autres approches, en évitant leurs inconvénients individuels. Aussi un std::string_view
est une bête beaucoup plus simple qu'un std::string
, surtout si ce dernier utilise SSO comme tous les modernes.
Au moins depuis C++ 14 (qui permettait généralement d'éliminer les allocations), les compilateurs pouvaient en théorie optimiser toutes les options par rapport à la dernière, à condition de disposer d'informations suffisantes (généralement disponibles pour l'exemple) et d'efforts, sous la comme si la règle . Mais nous n'en sommes pas encore là.
Non, compare()
ne nécessite pas la construction d'un std::string
pour const char*
opérandes.
Vous utilisez surcharge # 4 ici .
La comparaison avec le littéral de chaîne est la version "gratuite" que vous recherchez. Instanciation d'un std::string
ici, c'est complètement inutile.
D'après ma compréhension, cela signifie que nous devrons de toute façon construire un
std::string
Afin d'effectuer la comparaison, donc je soupçonne que les frais généraux seront les mêmes à la fin (bien que masqués par l'appel àoperator==
).
C'est là que ce raisonnement tourne mal. std::compare
n'a pas besoin d'allouer son opérande en tant que chaîne à terminaison nulle de style C pour fonctionner. Selon l'une des surcharges:
int compare( const CharT* s ) const; // (4)
4) Compare cette chaîne à la séquence de caractères terminée par un caractère nul commençant par le caractère pointé par
s
avec la longueurTraits::length(s)
.
Bien que l'allocation ou non soit un détail d'implémentation, il ne semble pas raisonnable qu'une comparaison de séquence le fasse.