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 _AD
et à _BC
cré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 0
pour des raisons octales). Cela peut être contourné en faisant en 1974-1/6_AD
sorte 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
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 unsize_t
paramètre supplémentaire . Ai-je raison?uint16_t
dont le comportement dépend de l'implémentation, par des types similairesuwrap16
etunum16
dont le comportement serait indépendant de l'implémentation, de sorte que compte tenuuwrap16 w=1; unum16 n=1;
des expressionsw-2
etn-2
donnerait(uwrap16)65535
et(int)-1
, respectivement [uint16_t
donnerait le premier résultat sur les systèmes oùint
est 16 bits, et le second sur les systèmes oùint
est plus grand]. Le plus gros problème que j'ai vu était la gestion des littéraux numériques.sizeof
retournent 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?Réponses:
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:
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).
la source
À 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 ++:
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:
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:
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:
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:
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.
la source
you can write 1+2i, but you still can't write a+bi, so there's absolutely no point
Même ignorer votrea+bi
exemple 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 écrire1.5f
et1000UL
pourquoi ne pourriez-vous pas écrire25i
ou même100101b
? 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 ++.Most of data still comes from IO
Il 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'écrirex = 2 * y ;
au lieu dex = Two * y
oùTwo
est 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.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.
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).
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.
la source
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
s
pourstd::string
eti
pour 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.
la source
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:
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:
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):
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.
la source
by the compiler at compile time
partie ... :-)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:
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.
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).
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).
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.
la source
La prise en charge de la vérification des dimensions au moment de la compilation est la seule justification requise.
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.
la source
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
... Je me demande comment vous exprimez cela aujourd'hui. Vous auriez une classe KG et une classe LB et vous compareriez des objets implicites:
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. :-)
la source
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.
la source
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.
la source
using single letters in global namespace will probably be considered bad practice
Mais 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 dansinline namespace
s que les utilisateurs peuvent importer en gros s'ils le souhaitent.J'ai utilisé des littéraux utilisateur pour les chaînes binaires comme ceci:
en utilisant le
std::string(str, n)
constructeur pour\0
ne 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::string
en faveur d'un wrapper pourstd::vector
.la source
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:
Ne justifie pas cette partie:
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:
Le mot-clé «auto» peut être justifié, peut-être seulement. Mais prenons juste C ++, car c'est mieux que C ++ 0x dans cet aspect.
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.
C'est peut-être quelque chose de simple, mais je ne pense pas que ce soit aussi simple en C ++ 0x.
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.
la source
std::complex<double> val(0, 3.14);
.