Pourquoi ne pas autoriser l’initialisation std :: ssortingng à partir d’un tableau de caractères?

En C ++, vous pouvez initialiser un object std::ssortingng partir d’un caractère char * et d’un caractère const char * , ce qui suppose implicitement que la chaîne se termine par le premier caractère NUL trouvé après le pointeur.

En C ++, les littéraux de chaîne sont toutefois des tableaux et un constructeur de modèle peut être utilisé pour obtenir la taille correcte, même si le littéral de chaîne contient des NUL incorporés. Voir par exemple l’implémentation de jouet suivante:

 #include  #include  #include  #include  struct Ssortingng { std::vector data; int size() const { return data.size(); } template Ssortingng(const T s); // Hack: the array will also possibly contain an ending NUL // we don't want... template Ssortingng(const char (&s)[N]) : data(s, s+N-(N>0 && s[N-1]=='\0')) {} // The non-const array removed as probably a lot of code // builds ssortingngs into char arrays and the convert them // implicitly to ssortingng objects. //template Ssortingng(char (&s)[N]) : data(s, s+N) {} }; // (one sortingcky part is that you cannot just declare a constructor // accepting a `const char *` because that would win over the template // constructor... here I made that constructor a template too but I'm // no template programming guru and may be there are better ways). template String::String(const char *s) : data(s, s+strlen(s)) {} int main(int argc, const char *argv[]) { String s1 = "Hello\0world\n"; printf("Length s1 -> %i\n", s1.size()); const char *s2 = "Hello\0world\n"; printf("Length s2 -> %i\n", Ssortingng(s2).size()); std::ssortingng s3 = "Hello\0world\n"; printf("std::ssortingng size = %i\n", int(s3.size())); return 0; } 

Existe-t-il une raison technique spécifique pour laquelle cette approche n’a pas été prise en compte pour la norme et qu’un littéral de chaîne avec NUL incorporé finisse par être tronqué lorsqu’il est utilisé pour initialiser un object std::ssortingng ?

Initialiser un std::ssortingng avec un littéral contenant nullbytes incorporé nécessite de transmettre à la fois le pointeur de départ et la longueur à un constructeur.

C’est plus simple s’il existe un modèle de constructeur dédié take-array-reference, mais comme vous le remarquerez

  • un tel modèle, avec uniquement l’argument de tableau, serait considéré comme une correspondance pire que le constructeur prenant simplement char const* , et

  • il ne serait pas clair si une valeur nulle finale finale devrait être incluse ou non.

Le premier point signifie que l’interface de code physique serait un constructeur unique basé sur un modèle, où seule la documentation (et non l’info-bulle de votre éditeur par exemple) raconterait toute l’histoire de ce qu’elle acceptait ou non. Un correctif consiste à introduire un argument de résolution factice supplémentaire. Cela réduit la commodité.

Le deuxième point est une opportunité pour introduire des bugs. L’utilisation la plus courante du constructeur serait sans doute les littéraux de chaîne ordinaires. Ensuite, de temps en temps, il serait utilisé pour les littéraux et / ou les tableaux avec nullbytes incorporés, mais curieusement avec le dernier caractère coupé.

Au lieu de cela on peut simplement nommer la valeur,

 char const data[] = "*.com\0*.exe\0*.bat\0*.cmd\0"; ssortingng s( data, data + sizeof( data ) ); // Including 2 nulls at end. 

Cela dit, lorsque j’ai défini mes propres classes de chaînes, j’ai inclus le constructeur take-array-argument, mais pour une raison très différente de la commodité. Ainsi, dans le cas d’un littéral, l’object chaîne peut simplement conserver ce pointeur, sans aucune copie, ce qui fournit non seulement une efficacité, mais également une sécurité (exactitude), par exemple pour les exceptions. Et un tableau de caractères const char est l’indication la plus claire du littéral que nous avons dans C ++ 11 et versions ultérieures.

Cependant, un std::ssortingng ne peut pas faire cela: ce n’est pas conçu pour ça.


Si cela est souvent fait, on pourrait définir une fonction comme celle-ci:

 using Size = ptrdiff_t; template< Size n > auto ssortingng_from_data( char const (&data)[n] ) -> std::ssortingng { return std::ssortingng( data, data + n ); } 

Alors on peut écrire juste

 ssortingng const s = ssortingng_from_data( "*.com\0*.exe\0*.bat\0*.cmd\0" ); 

Déni de responsabilité: aucun code touché ou vu par un compilateur.


[Cela manquait lors d’une première écriture, mais la réponse de Hurkyl le rappelait. En route pour le café!]

Un littéral de type chaîne C ++ 14 coupe le \0 final. Ainsi, avec un tel littéral, le précédent devrait inclure explicitement cette valeur nulle finale:

 ssortingng const s = "*.com\0*.exe\0*.bat\0*.cmd\0\0"s; 

En dehors de cela, les littéraux de type chaîne C ++ 14 semblent offrir le confort souhaité.

C ++ 14 introduit un suffixe pour les littéraux de chaîne afin de les transformer en objects std::ssortingng , de sorte que le cas d’utilisation principal n’est plus pertinent.

 #include  #include  using namespace std; using namespace std::literals; int main() { ssortingng foo = "Hello\0world\n"; ssortingng bar = "Hello\0world\n"s; cout << foo.size() << " " << bar.size() << endl; // 5 12 cout << foo << endl; // Hello cout << bar << endl; // Helloworld return 0; }