Quelles nouvelles fonctionnalités les littéraux définis par l'utilisateur ajoutent-ils au C ++?

139

C de 11 Introduit les littéraux définis par l' utilisateur qui permettra l'introduction de nouvelle syntaxe littérale basée sur les littéraux existants ( int, hex, string, float) de sorte que tout type sera en mesure d'avoir une présentation littérale.

Exemples:

// imaginary numbers
std::complex<long double> operator "" _i(long double d) // cooked form
{ 
    return std::complex<long double>(0, d); 
}
auto val = 3.14_i; // val = complex<long double>(0, 3.14)

// binary values
int operator "" _B(const char*); // raw form
int answer = 101010_B; // answer = 42

// std::string
std::string operator "" _s(const char* str, size_t /*length*/) 
{ 
    return std::string(str); 
}

auto hi = "hello"_s + " world"; // + works, "hello"_s is a string not a pointer

// units
assert(1_kg == 2.2_lb); // give or take 0.00462262 pounds

À première vue, cela a l'air très cool, mais je me demande à quel point c'est vraiment applicable, quand j'ai essayé de penser à avoir les suffixes _ADet à _BCcréer des dates, j'ai trouvé que c'était problématique en raison de l'ordre des opérateurs. 1974/01/06_ADévaluerait d'abord 1974/01(en clair int) et seulement plus tard 06_AD(sans parler d'août et de septembre devant être écrit sans le 0pour des raisons octales). Cela peut être contourné en faisant en 1974-1/6_ADsorte que la syntaxe soit telle que l'ordre d'évaluation des opérateurs fonctionne mais que ce soit maladroit.

Ma question se résume donc à ceci: pensez-vous que cette fonctionnalité se justifiera? Quels autres littéraux aimeriez-vous définir pour rendre votre code C ++ plus lisible?


Syntaxe mise à jour pour s'adapter au projet final de juin 2011

Motti
la source
8
Je vais voter pour fermer ça. Le titre est clairement enflammé.
Puppy
76
@DeadMG, si vous avez un problème avec le titre, vous pouvez le modifier. C'est un peu drôle d'essayer de fermer une question de 3 ans qui a 11 votes positifs et 8 favoris. (Pas la mention que l'étiquette sur ce site a changé au cours des 3 dernières années).
Motti
5
Je pense que vous avez une erreur dans vos exemples: string operator "" _s(const char*s);"ne peut pas être utilisé pour analyser "hello"_s". Il s'agit d'un littéral de chaîne et recherchera l'opérateur avec un size_tparamètre supplémentaire . Ai-je raison?
towi
1
Une chose sur laquelle je me suis demandé est de savoir s'il serait judicieux d'écrire «C portable» en C ++, en remplaçant des types comme uint16_tdont le comportement dépend de l'implémentation, par des types similaires uwrap16et unum16dont le comportement serait indépendant de l'implémentation, de sorte que compte tenu uwrap16 w=1; unum16 n=1;des expressions w-2et n-2donnerait (uwrap16)65535et (int)-1, respectivement [ uint16_tdonnerait le premier résultat sur les systèmes où intest 16 bits, et le second sur les systèmes où intest plus grand]. Le plus gros problème que j'ai vu était la gestion des littéraux numériques.
supercat
1
Être capable de faire interagir les littéraux numériques en douceur avec d'autres types numériques à comportement défini semblerait que cela devrait permettre d'utiliser ces types pour créer un langage où le code qui voulait effectuer des actions dépendant de l'implémentation pourrait le faire sans avoir à compter sur l'implémentation. comportements définis. Il y a quelques endroits où l'IDB sera toujours inévitable parce que des choses comme les différences de pointeur et sizeofretournent des types entiers dépendants de l'implémentation, mais la situation pourrait encore être bien meilleure qu'elle ne l'est. Que pensez-vous de ce concept?
supercat

Réponses:

71

Voici un cas où il y a un avantage à utiliser des littéraux définis par l'utilisateur au lieu d'un appel de constructeur:

#include <bitset>
#include <iostream>

template<char... Bits>
  struct checkbits
  {
    static const bool valid = false;
  };

template<char High, char... Bits>
  struct checkbits<High, Bits...>
  {
    static const bool valid = (High == '0' || High == '1')
                   && checkbits<Bits...>::valid;
  };

template<char High>
  struct checkbits<High>
  {
    static const bool valid = (High == '0' || High == '1');
  };

template<char... Bits>
  inline constexpr std::bitset<sizeof...(Bits)>
  operator"" _bits() noexcept
  {
    static_assert(checkbits<Bits...>::valid, "invalid digit in binary string");
    return std::bitset<sizeof...(Bits)>((char []){Bits..., '\0'});
  }

int
main()
{
  auto bits = 0101010101010101010101010101010101010101010101010101010101010101_bits;
  std::cout << bits << std::endl;
  std::cout << "size = " << bits.size() << std::endl;
  std::cout << "count = " << bits.count() << std::endl;
  std::cout << "value = " << bits.to_ullong() << std::endl;

  //  This triggers the static_assert at compile time.
  auto badbits = 2101010101010101010101010101010101010101010101010101010101010101_bits;

  //  This throws at run time.
  std::bitset<64> badbits2("2101010101010101010101010101010101010101010101010101010101010101_bits");
}

L'avantage est qu'une exception d'exécution est convertie en une erreur de compilation. Vous ne pouviez pas ajouter l'assertion statique au processeur de l'ensemble de bits en prenant une chaîne (du moins pas sans arguments de modèle de chaîne).

emsr
la source
7
Vous pouvez faire la même chose en donnant à std :: bitset un constructeur constexpr approprié.
Nicol Bolas
1
@NicolBolas Vous avez raison. Je suis en fait surpris qu'il n'y en ait pas. Peut-être devrions-nous en proposer un ou deux pour 2014 s'il n'est pas trop tard.
emsr
192

À première vue, il semble s'agir d'un simple sucre syntaxique.

Mais en regardant plus en profondeur, nous voyons que c'est plus que du sucre syntaxique, car il étend les options de l'utilisateur C ++ pour créer des types définis par l'utilisateur qui se comportent exactement comme des types intégrés distincts. En cela, ce petit "bonus" est un ajout très intéressant du C ++ 11 au C ++.

En avons-nous vraiment besoin en C ++?

Je vois peu d'utilisations dans le code que j'ai écrit ces dernières années, mais ce n'est pas parce que je ne l'ai pas utilisé en C ++ que ce n'est pas intéressant pour un autre développeur C ++ .

Nous avions utilisé en C ++ (et en C, je suppose), des littéraux définis par le compilateur, pour taper des nombres entiers comme des entiers courts ou longs, des nombres réels comme float ou double (ou même long double), et des chaînes de caractères comme des caractères normaux ou larges .

En C ++, nous avions la possibilité de créer nos propres types (ie classes), sans potentiellement aucun surcoût (inlining, etc.). Nous avons eu la possibilité d'ajouter des opérateurs à leurs types, de les faire se comporter comme des types intégrés similaires, ce qui permet aux développeurs C ++ d'utiliser des matrices et des nombres complexes aussi naturellement qu'ils l'auraient fait si ceux-ci avaient été ajoutés au langage lui-même. Nous pouvons même ajouter des opérateurs de cast (ce qui est généralement une mauvaise idée, mais parfois, c'est juste la bonne solution).

Nous avons encore manqué une chose pour que les types d'utilisateurs se comportent comme des types intégrés: les littéraux définis par l'utilisateur.

Donc, je suppose que c'est une évolution naturelle pour le langage, mais pour être aussi complète que possible: " Si vous voulez créer un type, et que vous voulez qu'il se comporte autant que possible comme un type intégré, voici les outils. .. "

Je suppose que c'est très similaire à la décision de .NET de faire de chaque primitive une structure, y compris les booléens, les entiers, etc., et que toutes les structures dérivent d'Object. Cette décision à elle seule place .NET bien au-delà de la portée de Java lorsqu'il travaille avec des primitives, quel que soit le nombre de hacks de boxe / déballage que Java ajoutera à sa spécification.

En avez-vous vraiment besoin en C ++?

C'est à VOUS de répondre à cette question. Pas Bjarne Stroustrup. Pas Herb Sutter. Pas n'importe quel membre du comité de normalisation C ++. C'est pourquoi vous avez le choix en C ++ , et ils ne limiteront pas une notation utile aux seuls types intégrés.

Si vous en avez besoin, c'est un ajout bienvenu. Si vous ne le faites pas, eh bien ... Ne l'utilisez pas. Cela ne vous coûtera rien.

Bienvenue dans C ++, le langage où les fonctionnalités sont facultatives.

Gonflé??? Montrez-moi vos complexes !!!

Il y a une différence entre gonflé et complexe (jeu de mots).

Comme montré par Niels à Quelles nouvelles fonctionnalités les littéraux définis par l'utilisateur ajoutent-ils au C ++? , être capable d'écrire un nombre complexe est l'une des deux fonctionnalités ajoutées "récemment" en C et C ++:

// C89:
MyComplex z1 = { 1, 2 } ;

// C99: You'll note I is a macro, which can lead
// to very interesting situations...
double complex z1 = 1 + 2*I;

// C++:
std::complex<double> z1(1, 2) ;

// C++11: You'll note that "i" won't ever bother
// you elsewhere
std::complex<double> z1 = 1 + 2_i ;

Désormais, les types C99 "double complex" et C ++ "std :: complex" peuvent être multipliés, ajoutés, soustraits, etc., en utilisant la surcharge d'opérateurs.

Mais dans C99, ils ont simplement ajouté un autre type en tant que type intégré et une prise en charge intégrée de la surcharge des opérateurs. Et ils ont ajouté une autre fonctionnalité littérale intégrée.

En C ++, ils ont juste utilisé les fonctionnalités existantes du langage, ont vu que la fonctionnalité littérale était une évolution naturelle du langage, et l'ont donc ajoutée.

En C, si vous avez besoin de la même amélioration de notation pour un autre type, vous n'avez pas de chance jusqu'à ce que votre lobbying ajoute vos fonctions d'onde quantique (ou points 3D, ou tout autre type de base que vous utilisez dans votre domaine de travail) au La norme C en tant que type intégré réussit.

En C ++ 11, vous pouvez le faire vous-même:

Point p = 25_x + 13_y + 3_z ; // 3D point

Est-ce gonflé? Non , le besoin est là, comme le montre la façon dont les complexes C et C ++ ont besoin d'un moyen de représenter leurs valeurs complexes littérales.

Est-il mal conçu? Non , il est conçu comme toutes les autres fonctionnalités C ++, avec l'extensibilité à l'esprit.

Est-ce uniquement à des fins de notation? Non , car cela peut même ajouter une sécurité de type à votre code.

Par exemple, imaginons un code orienté CSS:

css::Font::Size p0 = 12_pt ;       // Ok
css::Font::Size p1 = 50_percent ;  // Ok
css::Font::Size p2 = 15_px ;       // Ok
css::Font::Size p3 = 10_em ;       // Ok
css::Font::Size p4 = 15 ;         // ERROR : Won't compile !

Il est alors très facile d'imposer un typage fort à l'attribution des valeurs.

Est-ce dangereux?

Bonne question. Ces fonctions peuvent-elles être espacées de noms? Si oui, alors Jackpot!

Quoi qu'il en soit, comme tout, vous pouvez vous tuer si un outil n'est pas utilisé correctement . C est puissant et vous pouvez vous tirer la tête si vous utilisez mal le pistolet C. C ++ a le pistolet C, mais aussi le scalpel, le taser et tout autre outil que vous trouverez dans la boîte à outils. Vous pouvez mal utiliser le scalpel et vous saigner à mort. Ou vous pouvez créer un code très élégant et robuste.

Alors, comme toute fonctionnalité C ++, en avez-vous vraiment besoin? C'est la question à laquelle vous devez répondre avant de l'utiliser en C ++. Sinon, cela ne vous coûtera rien. Mais si vous en avez vraiment besoin, au moins, la langue ne vous laissera pas tomber.

L'exemple de la date?

Votre erreur, il me semble, est que vous mélangez des opérateurs:

1974/01/06AD
    ^  ^  ^

Cela ne peut pas être évité, car / étant un opérateur, le compilateur doit l'interpréter. Et, AFAIK, c'est une bonne chose.

Pour trouver une solution à votre problème, j'écrirais le littéral d'une autre manière. Par exemple:

"1974-01-06"_AD ;   // ISO-like notation
"06/01/1974"_AD ;   // french-date-like notation
"jan 06 1974"_AD ;  // US-date-like notation
19740106_AD ;       // integer-date-like notation

Personnellement, je choisirais l'entier et les dates ISO, mais cela dépend de VOS besoins. C'est tout l'intérêt de laisser l'utilisateur définir ses propres noms littéraux.

Paercebal
la source
1
Merci, moi et mes autres personnalités alternatives ont été découverts. Plus sérieusement, j'ai écrit ceci seul, mais j'utilise peut-être une expression de ma langue maternelle et ils ne se traduisent pas bien en anglais.
paercebal
Quant aux "différentes parties", comme le montrent leurs titres, désolé, je suppose que cela organise un article assez long. Quant au texte en gras, c'est le résumé du paragraphe dans lequel ils se trouvent. Les personnes ne voulant que des informations sans justification peuvent limiter leur lecture sur les titres et le texte en gras.
paercebal
3
+1. Vraiment belle explication. Nous attendons que cela soit mis en œuvre. C'est vraiment important pour nous. Nous travaillons sur MDE (Model-Driven Engineering) et trouvons cela une nécessité. J'ajoute une réponse ci-dessous pour expliquer notre cas.
Diego Sevilla
9
@TGV:: you can write 1+2i, but you still can't write a+bi, so there's absolutely no pointMême ignorer votre a+biexemple est ridicule, le fait que vous le perceviez comme "basse fréquence" ne signifie pas que tout le monde le fait. . . En regardant l'image dans son ensemble, le but est de s'assurer que les objets définis par l'utilisateur peuvent être autant que possible considérés comme des citoyens de première classe du langage, tout comme les types intégrés. Alors, si vous savez écrire 1.5fet 1000ULpourquoi ne pourriez-vous pas écrire 25iou même 100101b? Contrairement à C et Java, les types d'utilisateurs ne doivent pas être considérés comme des citoyens de seconde classe du langage en C ++.
paercebal
3
@Anton:: Most of data still comes from IOIl y a beaucoup de valeurs codées en dur dans le code. Regardez tous les booléens, tous les entiers, tous les doubles qui entrent dans le code, car il est plus pratique d'écrire x = 2 * y ;au lieu de x = Two * yTwoest une constante fortement typée . Les littéraux définis par l'utilisateur nous permettent de mettre un type dessus, d'écrire: x = 2_speed * y ;et de demander au compilateur de vérifier que le calcul a du sens. . . Tout repose sur une frappe forte. . . Peut-être que vous ne l'utiliserez pas. Mais je le ferai certainement, dès que je pourrai utiliser un compilateur compatible C ++ 11 au travail.
paercebal
36

C'est très bien pour le code mathématique. Hors de mon esprit, je peux voir l'utilisation des opérateurs suivants:

deg pour les diplômes. Cela rend l'écriture d'angles absolus beaucoup plus intuitive.

double operator ""_deg(long double d)
{ 
    // returns radians
    return d*M_PI/180; 
}

Il peut également être utilisé pour diverses représentations en virgule fixe (qui sont encore utilisées dans le domaine du DSP et du graphisme).

int operator ""_fix(long double d)
{ 
    // returns d as a 1.15.16 fixed point number
    return (int)(d*65536.0f); 
}

Ceux-ci ressemblent à de jolis exemples d'utilisation. Ils aident à rendre les constantes du code plus lisibles. C'est un autre outil pour rendre le code illisible également, mais nous avons déjà tellement d'abus d'outils qu'un de plus ne fait pas beaucoup de mal.

Nils Pipenbrinck
la source
1
"mais nous avons déjà tellement d’outils abusifs que qu'un de plus ne fait pas beaucoup de mal. " Wow, j'espère que ce n'est pas la philosophie derrière toutes ces fonctionnalités c ++ [x] 1234567890 qui ont inondé récemment. Imaginez avoir à apprendre une bibliothèque qui utilise tout cela, plus dix formats de fichiers pour la configuration et deux outils pour le pré et le post-traitement de votre code ...
masterxilo
@masterxilo Bien sûr, en réalité, votre exemple est absurde: les UDL ne sont pas plus difficiles à apprendre que les fonctions, car c'est juste du sucre de syntaxe pour eux - et de plus, toute bonne bibliothèque n'utilise que les fonctionnalités nécessaires pour améliorer l'UX - et documente exactement ce qu'elle tous les moyens. Si quelqu'un utilise abusivement une fonctionnalité pour générer du code illisible (en supposant que cela puisse être évité dans son domaine de travail ...), cela ne met pas cette fonctionnalité en défaut, juste son utilisation. De plus, l'illisibilité d'une personne est le pain et le beurre d'une autre. Ce sont toutes les opinions - et options . Si vous ne les aimez pas, ne vous inquiétez pas! Vous n'êtes pas obligé de les utiliser. D'autres peuvent .
underscore_d
17

Les UDL sont espacés de noms (et peuvent être importés en utilisant des déclarations / directives, mais vous ne pouvez pas explicitement un espace de noms comme un littéral 3.14std::i), ce qui signifie qu'il n'y aura (espérons-le) pas une tonne de conflits.

Le fait qu'ils puissent réellement être modélisés (et constexpr'd) signifie que vous pouvez faire des choses assez puissantes avec les UDL. Les auteurs de Bigint seront vraiment heureux, car ils peuvent enfin avoir des constantes arbitrairement grandes, calculées au moment de la compilation (via constexpr ou des templates).

Je suis juste triste que nous ne voyions pas quelques littéraux utiles dans le standard (d'après son apparence), comme spour std::stringet ipour l'unité imaginaire.

Le temps de codage qui sera économisé par les UDL n'est en fait pas si élevé, mais la lisibilité sera considérablement augmentée et de plus en plus de calculs pourront être déplacés vers le temps de compilation pour une exécution plus rapide.

coppro
la source
Merci d'avoir clarifié le point sur les espaces de noms ... Je me posais la question.
Nathan Reed
12

Permettez-moi d'ajouter un peu de contexte. Pour notre travail, les littéraux définis par l'utilisateur sont indispensables. Nous travaillons sur MDE (Model-Driven Engineering). Nous voulons définir des modèles et des métamodèles en C ++. Nous avons en fait implémenté un mappage d'Ecore vers C ++ ( EMF4CPP ).

Le problème vient de la possibilité de définir des éléments de modèle comme des classes en C ++. Nous adoptons l'approche de transformer le métamodèle (Ecore) en modèles avec des arguments. Les arguments du modèle sont les caractéristiques structurelles des types et des classes. Par exemple, une classe avec deux attributs int serait quelque chose comme:

typedef ::ecore::Class< Attribute<int>, Attribute<int> > MyClass;

Cependant, il s'avère que chaque élément d'un modèle ou d'un métamodèle a généralement un nom. Nous aimerions écrire:

typedef ::ecore::Class< "MyClass", Attribute< "x", int>, Attribute<"y", int> > MyClass;

MAIS, C ++, ni C ++ 0x ne le permettent pas, car les chaînes sont interdites comme arguments dans les modèles. Vous pouvez écrire le nom char par char, mais c'est un vrai gâchis. Avec des littéraux appropriés définis par l'utilisateur, nous pourrions écrire quelque chose de similaire. Disons que nous utilisons "_n" pour identifier les noms des éléments du modèle (je n'utilise pas la syntaxe exacte, juste pour faire une idée):

typedef ::ecore::Class< MyClass_n, Attribute< x_n, int>, Attribute<y_n, int> > MyClass;

Enfin, avoir ces définitions comme modèles nous aide beaucoup à concevoir des algorithmes pour parcourir les éléments du modèle, les transformations du modèle, etc. qui sont vraiment efficaces, car les informations de type, l'identification, les transformations, etc. sont déterminées par le compilateur au moment de la compilation.

Diego Séville
la source
4
J'aime beaucoup la by the compiler at compile timepartie ... :-)
paercebal
12

Bjarne Stroustrup parle des UDL dans cette conférence C ++ 11 , dans la première section sur les interfaces riches en types, environ 20 minutes.

Son argument de base pour les UDL prend la forme d'un syllogisme:

  1. Les types "triviaux", c'est-à-dire les types primitifs intégrés, ne peuvent détecter que les erreurs de type triviales. Les interfaces avec des types plus riches permettent au système de types de détecter plus de types d'erreurs.

  2. Les types d'erreurs de type que le code richement typé peut détecter ont un impact sur le code réel. (Il donne l'exemple du Mars Climate Orbiter, qui a échoué en raison d'une erreur de dimensions dans une constante importante).

  3. Dans le code réel, les unités sont rarement utilisées. Les gens ne les utilisent pas, car il est trop coûteux d'engager des calculs à l'exécution ou une surcharge de mémoire pour créer des types riches, et l'utilisation d'un code d'unité basé sur un modèle C ++ préexistant est si notoirement moche que personne ne l'utilise. (Empiriquement, personne ne l'utilise, même si les bibliothèques existent depuis une décennie).

  4. Par conséquent, afin d'amener les ingénieurs à utiliser des unités dans du code réel, nous avions besoin d'un périphérique qui (1) n'entraîne aucune surcharge d'exécution et (2) est notablement acceptable.

maçon
la source
9

La prise en charge de la vérification des dimensions au moment de la compilation est la seule justification requise.

auto force = 2_N; 
auto dx = 2_m; 
auto energy = force * dx; 

assert(energy == 4_J); 

Voir par exemple PhysUnits-CT-Cpp11 , une petite bibliothèque d'en-tête uniquement C ++ 11, C ++ 14 pour l'analyse dimensionnelle au moment de la compilation et la manipulation et la conversion d'unité / quantité. Plus simple que Boost. , prend en charge les littéraux de symbole d'unité tels que m, g, s, les préfixes métriques tels que m, k, M, ne dépend que de la bibliothèque C ++ standard, SI uniquement, les puissances intégrales des dimensions.

Martin Moene
la source
Ou voyez les unités , une bibliothèque d'analyse dimensionnelle et de conversion d' unités au moment de la compilation, en-tête uniquement, construite sur c ++ 14 sans dépendances par Nic Holthaus .
Martin Moene
6

Hmm ... Je n'ai pas encore pensé à cette fonctionnalité. Votre échantillon a été bien pensé et est certainement intéressant. C ++ est très puissant tel qu'il est actuellement, mais malheureusement, la syntaxe utilisée dans les morceaux de code que vous lisez est parfois trop complexe. La lisibilité est, sinon tout, du moins beaucoup. Et une telle fonctionnalité serait conçue pour plus de lisibilité. Si je prends votre dernier exemple

assert(1_kg == 2.2_lb); // give or take 0.00462262 pounds

... Je me demande comment vous exprimez cela aujourd'hui. Vous auriez une classe KG et une classe LB et vous compareriez des objets implicites:

assert(KG(1.0f) == LB(2.2f));

Et cela ferait aussi bien. Avec des types qui ont des noms plus longs ou des types que vous n'espérez pas avoir un constructeur aussi sympa pour sans écrire un adaptateur, cela pourrait être un ajout intéressant pour la création et l'initialisation d'objet implicite à la volée. D'un autre côté, vous pouvez déjà créer et initialiser des objets à l'aide de méthodes.

Mais je suis d'accord avec Nils sur les mathématiques. Les fonctions de trigonométrie C et C ++, par exemple, nécessitent une entrée en radians. Je pense en degrés cependant, donc une conversion implicite très courte comme Nils publiée est très agréable.

En fin de compte, ce sera du sucre syntaxique, mais cela aura un léger effet sur la lisibilité. Et il sera probablement plus facile d'écrire certaines expressions aussi (sin (180.0deg) est plus facile à écrire que sin (deg (180.0)). Et puis il y aura des gens qui abuseront du concept. Mais alors, les personnes abusives de langage devraient utiliser langages très restrictifs plutôt que quelque chose d'aussi expressif que C ++.

Ah, mon message ne dit fondamentalement rien sauf: ça va aller, l'impact ne sera pas trop grand. Ne nous inquiétons pas. :-)

mstrobl
la source
5
Vos parenthèses sont déséquilibrées! Désolé, mon TOC me déteste aussi.
X-Istence
3

Je n'ai jamais eu besoin ni voulu de cette fonctionnalité (mais cela pourrait être l' effet Blub ). Ma réaction instinctive est que c'est boiteux, et susceptible de plaire aux mêmes personnes qui pensent que c'est cool de surcharger l'opérateur + pour toute opération qui pourrait à distance être interprétée comme un ajout.

pétillant
la source
Je confirme: Article très intéressant.
paercebal
2

Le C ++ est généralement très strict quant à la syntaxe utilisée - à l'exception du préprocesseur, vous ne pouvez pas utiliser grand-chose pour définir une syntaxe / grammaire personnalisée. Par exemple, nous pouvons surcharger les operatos existants, mais nous ne pouvons pas en définir de nouveaux - IMO c'est très en phase avec l'esprit du C ++.

Cela ne me dérange pas d'avoir un code source plus personnalisé - mais le point choisi me semble très isolé, ce qui me déroute le plus.

Même l'utilisation prévue peut rendre la lecture du code source beaucoup plus difficile: une seule lettre peut avoir des effets secondaires de grande envergure qui ne peuvent en aucun cas être identifiés à partir du contexte. Avec une symétrie vers u, l et f, la plupart des développeurs choisiront des lettres uniques.

Cela peut également transformer la portée en problème, l'utilisation de lettres uniques dans l'espace de noms global sera probablement considérée comme une mauvaise pratique, et les outils censés mélanger plus facilement les bibliothèques (espaces de noms et identificateurs descriptifs) iront probablement à l'encontre de son objectif.

Je vois des avantages en combinaison avec "auto", également en combinaison avec une bibliothèque d' unités comme les unités boost , mais pas assez pour mériter cette addition.

Je me demande cependant quelles idées intelligentes nous proposons.

Peterchen
la source
1
using single letters in global namespace will probably be considered bad practiceMais cela n'a aucune pertinence: (A) les UDL doivent être définies dans une portée d'espace de noms (non globale) ... probablement parce que (B) elles doivent consister en un trait de soulignement puis> = 1 lettre, pas seulement la lettre, et de tels identifiants dans les NS globales sont réservées à l'implémentation. C'est au moins 2 points contre l'idée que les UDL génèrent naturellement de la confusion. Quant au fait d'avoir à étendre l'espace de noms réduisant l'utilité de la fonctionnalité, c'est pourquoi, par exemple, le stdlib les déclare dans inline namespaces que les utilisateurs peuvent importer en gros s'ils le souhaitent.
underscore_d
2

J'ai utilisé des littéraux utilisateur pour les chaînes binaires comme ceci:

 "asd\0\0\0\1"_b

en utilisant le std::string(str, n)constructeur pour \0ne pas couper la chaîne en deux. (Le projet fait beaucoup de travail avec différents formats de fichiers.)

Cela a également été utile lorsque j'ai abandonné std::stringen faveur d'un wrapper pour std::vector.

rr-
la source
-5

Le bruit de ligne dans cette chose est énorme. C'est aussi horrible à lire.

Faites-moi savoir, ont-ils raisonné ce nouvel ajout de syntaxe avec des exemples? Par exemple, ont-ils quelques programmes qui utilisent déjà C ++ 0x?

Pour moi, cette partie:

auto val = 3.14_i

Ne justifie pas cette partie:

std::complex<double> operator ""_i(long double d) // cooked form
{ 
    return std::complex(0, d);
}

Pas même si vous utilisiez également la syntaxe i dans 1000 autres lignes. Si vous écrivez, vous écrivez probablement 10000 lignes de quelque chose d'autre en même temps. Surtout quand vous écrirez probablement encore partout ceci:

std::complex<double> val = 3.14i

Le mot-clé «auto» peut être justifié, peut-être seulement. Mais prenons juste C ++, car c'est mieux que C ++ 0x dans cet aspect.

std::complex<double> val = std::complex(0, 3.14);

C'est comme… aussi simple que cela. Même si toutes les parenthèses std et pointues sont tout simplement nulles si vous les utilisez partout. Je ne commence pas à deviner quelle syntaxe il y a dans C ++ 0x pour transformer std :: complex sous complex.

complex = std::complex<double>;

C'est peut-être quelque chose de simple, mais je ne pense pas que ce soit aussi simple en C ++ 0x.

typedef std::complex<double> complex;

complex val = std::complex(0, 3.14);

Peut-être? > :)

Quoi qu'il en soit, le point est: d'écrire 3.14i au lieu de std :: complex (0, 3.14); ne vous fait pas gagner beaucoup de temps dans l'ensemble, sauf dans quelques cas super spéciaux.

Gai
la source
10
@Cheery: Pour vous, "auto val = 3.14i" ne justifie pas le code écrit pour le supporter. Je pourrais répondre à cela, pour moi "printf ("% i ", 25)" ne justifie pas le code écrit pour printf. Peut-tu discerner une structure logique?
paercebal
5
@Cheery: "Le bruit de ligne dans ce truc est énorme". Non, ce n'est pas ... "C'est aussi horrible à lire". Votre argument subjectif est intéressant, mais vous devriez jeter un œil à la surcharge des opérateurs en général pour voir que le code de cette fonctionnalité est loin d'être surprenant / choquant ... Pour un développeur C ++
paercebal
3
auto aidera à la lisibilité. envisagez d'utiliser des interateurs dans une boucle for: for (auto it = vec.begin (); it! = vec.end (); ++ it) ... Je connais for_each, mais je n'aime pas avoir à créer un foncteur pour l'utiliser .
KitsuneYMG
1
@kts: Avec C ++ 1x, nous aurons lambda et la plage pour
Joe D
3
Si votre ligne de C ++ est meilleure que C ++ 0x, alors ma ligne est encore meilleure. Juste écrire: std::complex<double> val(0, 3.14);.
Ben Voigt