69
votes

std :: string s1 {"moderne c ++", 3} vs std :: string s1 {str, 3}

La sortie du code suivant me confond:

> S1: Mod
> S2: ern C++

sortie:

const std::string str = "Modern C++";

std::string s1 {"Modern C++", 3};
std::string s2 {str, 3};

std::cout << "S1: " << s1 << "\n";
std::cout << "S2: " << s2 << "\n";

Quelqu'un peut-il expliquer ce résultat?


10 commentaires

S1 et S2 devraient être les mêmes à moins que je manque quelque chose.


Avez-vous lu la référence sur std :: string < / code> constructeurs? "C ++ moderne" n'est pas un std :: string mais un tableau de caractères (qui est automatiquement converti en pointeur de char), et il y a un constructeur différent pour ceux-ci.


Bien sûr. La référence vous le dirait. Et je suis sûr qu'il y a une raison incroyablement intelligente qui ne fait pas la même chose, ce que le comité de bibliothèque C ++ me prouverait en utilisant une logique et un raisonnement impeccables. Et pourtant, ce n'est qu'une raison de plus - bien que petite - que la bibliothèque standard C ++ reste inutile, voire frustrante, à utiliser.


@Holyblackcat Néanmoins, le résultat devrait être le même, merci quand même.


Oui, ça devrait probablement.


La "raison incroyablement intelligente" est que Std :: String a été initialement conçu il y a des décennies, donc avec le recul, certains de ses constructeurs et fonctions ne correspondent pas à ce que nous considérerions comme une interface intuitive moderne


@ M.M.: Cela, en plus du fait que les chaînes de style C ont été conçues il y a encore plus longtemps, et std :: string a dû travailler avec leurs conventions. Cela dit, aurait probablement dû rester avec la même sémantique de chaîne de style C pour les deux, au lieu d'avoir une position être un point de départ efficace pour std :: string , et un point final efficace pour char * , donc votre point se dresse toujours, en soulignant simplement que les contraintes de compatibilité dans ce royaume sont antérieures à std :: string .


C'est vraiment un exemple magnifiquement parfait de la raison pour laquelle certains pourraient prétendre que C ++ "suce".


@ M.M La cohérence et la logique ne sont guère des inventions récentes


TBH, je pense que le vrai problème est que chaque classe a un nom de constructeur - le nom de classe. Vous ne pouvez pas avoir différents constructeurs pour la même classe avec des noms différents, et ne pouvez avoir que plusieurs constructeurs en raison de la surcharge. Différentes surcharges avec le même nom faisant des choses différentes est malodorante, mais c'est ce que nous sommes coincés pour les constructeurs. Sauf que ce que vous devriez probablement faire, c'est se déplacer-construction d'une fonction nommée qui renvoie la valeur que vous voulez - n'utilisez pas du tout tous les constructeurs déroutants, ce sont des reliques obsolètes imo depuis un âge avant de déplacer la sémantique.


4 Réponses :


66
votes

De:

https://en.cppreference.com/w / cpp / string / basic_string / basic_string

basic_string( const basic_string& other,
          size_type pos,
          const Allocator& alloc = Allocator() );

utilise le constructeur suivant:

std::string s2 {str, 3};

prend donc 3 Chars à obtenir mod .

basic_string( const CharT* s,
          size_type count,
          const Allocator& alloc = Allocator() );

utilisera le constructeur suivant:

std::string s1 {"Modern C++", 3};

Ainsi, la prise La chaîne de la position 3 donnant: ERN C ++ .


4 commentaires

Merci. Le premier est size_type count et le second est size_type pos .


Il faut demander pourquoi ils ajouteraient jamais deux constructeurs avec un comportement aussi différent. C'est comme s'il était pavé avec aucune pensée du tout.


@PolyGnome: "Coblé sans aucune pensée" décrit essentiellement l'ensemble de std :: string . Il tient également compte du mélange de méthodes basées sur l'indice et basées sur les itérateurs - String a été développé indépendamment du STL, puis rétroactivement augmenté pour correspondre - et en général a trop de "méthodes de commodité. J'aime toujours gotw # 84: monoliths unsetrung qui plonge dans une API de chaîne minimale complétée par des fonctions libres.


Y a-t-il même un cas d'utilisation pour ce constructeur de sous-chaîne qui ne serait pas servi par la méthode substr ?



35
votes

L'un appelle string (char const *, count) , l'autre chaîne (chaîne const &, pos) .

L'un obtient les 3 premiers caractères d'un tampon, l'autre tous les personnages après le 3.

En effet, C ++ a des tampons de caractères bruts et des chaînes STD. "Ce n'est pas une chaîne std :: string" . "Ceci est une chaîne std" s , std :: string so_is = "this"; .

std :: string a plus de 30 ans, et il a été ajouté à la langue C ++ sans soin suffisant (contrairement au STL, qui a subi plus d'itérations avant d'être ajoutée).

Son interface est honnêtement trop riche, et vous pouvez rencontrer des trucs comme celui-ci; Plusieurs surcharges qui conduisent à des résultats déroutants.


0 commentaires

7
votes

quelqu'un peut m'expliquer pourquoi est-ce?

Cela est dû à std :: string ayant des constructeurs qui ne devraient vraiment pas (@orr expliquée Les détails). Et il ne devrait pas avoir ces constructeurs parce que:

  • Leur effet est facilement réalisable en utilisant idioms du constructeur nommés / << Code> std :: string Méthodes et constructeurs existants - sans frais supplémentaires (en C ++ 11 au moins), et
  • Il n'est pas évident et trivial de comprendre comment les arguments du constructeur sont utilisés en regardant simplement l'invocation du constructeur.
  • Ce n'est pas le seul cas de la bibliothèque standard avec de tels constructeurs sous-deux (IMHO); std :: vector est (in) célèbre Pour trop de variété de constructeurs et déroutant / sementiers de constructeurs déroutants.

    Leçons de vie:

    • Sélimp sur les constructeurs; Tous les groupes de valeurs couramment utilisés ne sont pas utilisés pour construire un objet de votre classe ne méritent pas leur propre constructeur;
    • Au lieu de cela, utilisez idioms du constructeur nommés .
    • Demandez à votre réviseur de code ou à une autre partie moins biaisée de lire une invocation de vos constructeurs, pour évaluer si le sens de chacun est assez évident.

    0 commentaires

    3
    votes

    au cas où vous souhaitez obtenir la même sortie que

    mod pour S1

    mod pour S2

    vous Peut utiliser un pointeur charbon pour Str, par exemple

     Mod
    
     Mod
    

    La sortie

    char * str = "Modern C++";
    
    std::string s1 {"Modern C++", 3};
    std::string s2 {str, 3};
    
    std::cout << "S1: " << s1 << "\n";
    std::cout << "S2: " << s2 << "\n";
    


    4 commentaires

    Pour que cela fonctionne, la première ligne doit être: char str [] = "C ++ moderne"; ou char str [] {"C ++ moderne"};


    Essayez-le, pour moi, cela a très bien fonctionné. En C ++ / C, vous pouvez également déclarer et initialiser un tableau de caractères avec un pointeur. char * str = "C ++ moderne"; Utilisez STR [Valeur] pour faire passer car STR est maintenant un tableau.


    Je crois que la syntaxe sans [] est char ** str.


    char * str = "C ++ moderne"; est, ironiquement, un C ++ moderne non valide. const est requis à C ++ 11. wandbox.org/permlink/e3ta4tkbrkbooull (gcc) / wandbox.org/permlink/tftryb565svyhmlb (clang)