Aucun amour C ++ quand il s'agit de la ligne de questions «fonctionnalités cachées»? Je pensais que je le jetterais là-bas. Quelles sont certaines des fonctionnalités cachées de C ++?
c++
hidden-features
Craig H
la source
la source
Réponses:
La plupart des programmeurs C ++ sont familiers avec l'opérateur ternaire:
Cependant, ils ne réalisent pas qu'il peut être utilisé comme une lvalue:
qui est un raccourci pour
Utiliser avec précaution :-)
la source
(value ? function1 : function2)()
.function1
etfunction2
sont implicitement convertis en pointeurs de fonction, et le résultat est implicitement reconverti.Vous pouvez placer des URI dans la source C ++ sans erreur. Par exemple:
la source
goto
, ce que C ++ a). Tout ce qui suit deux barres obliques est un commentaire. Par conséquent, avechttp://stackoverflow.com
,http
est une étiquette (vous pourriez théoriquement écriregoto http;
), et//stackoverflow.com
n'est qu'un commentaire de fin de ligne. Les deux sont du C ++ légal, donc la construction se compile. Cela ne fait rien de vaguement utile, bien sûr.goto http;
ne suit pas réellement l'URL. :(Les programmeurs C ++ préfèrent éviter les pointeurs à cause des bogues qui peuvent être introduits.
Le C ++ le plus cool que j'aie jamais vu? Littéraux analogiques.
la source
Je suis d'accord avec la plupart des articles: C ++ est un langage multi-paradigme, donc les fonctionnalités "cachées" que vous trouverez (autres que les "comportements indéfinis" que vous devriez éviter à tout prix) sont des utilisations intelligentes des installations.
La plupart de ces fonctionnalités ne sont pas des fonctionnalités intégrées du langage, mais des fonctionnalités basées sur une bibliothèque.
Le plus important est le RAII , souvent ignoré depuis des années par les développeurs C ++ venant du monde C. La surcharge des opérateurs est souvent une fonctionnalité mal comprise qui permet à la fois un comportement de type tableau (opérateur d'indice), des opérations de type pointeur (pointeurs intelligents) et des opérations de type intégré (multiplication des matrices.
L'utilisation de l' exception est souvent difficile, mais avec un peu de travail, elle peut produire un code vraiment robuste grâce à des spécifications de sécurité d'exception (y compris du code qui n'échouera pas, ou qui aura des fonctionnalités de type commit qui réussiront ou reviendront à son état d'origine).
La fonctionnalité "cachée" la plus connue de C ++ est la métaprogrammation de modèles , car elle vous permet d'exécuter partiellement (ou totalement) votre programme au moment de la compilation au lieu de l'exécution. C'est difficile, cependant, et vous devez avoir une solide maîtrise des modèles avant de l'essayer.
D'autres utilisent le paradigme multiple pour produire des «façons de programmer» en dehors de l'ancêtre de C ++, c'est-à-dire C.
En utilisant des foncteurs , vous pouvez simuler des fonctions, avec la sécurité de type supplémentaire et étant avec état. En utilisant le modèle de commande , vous pouvez retarder l'exécution du code. La plupart des autres modèles de conception peuvent être facilement et efficacement implémentés en C ++ pour produire des styles de codage alternatifs qui ne sont pas censés être dans la liste des «paradigmes officiels du C ++».
En utilisant des modèles , vous pouvez produire du code qui fonctionnera sur la plupart des types, y compris celui que vous avez pensé au début. Vous pouvez également augmenter la sécurité des types (comme un malloc / realloc / free). Les fonctionnalités des objets C ++ sont vraiment puissantes (et donc dangereuses si elles sont utilisées avec négligence), mais même le polymorphisme dynamique a sa version statique en C ++: le CRTP .
J'ai trouvé que la plupart des livres de type " C ++ efficace " de Scott Meyers ou des livres de type " C ++ exceptionnel " de Herb Sutter sont à la fois faciles à lire et sont assez riches d'informations sur les fonctionnalités connues et moins connues du C ++.
Parmi mes préférés, il y en a un qui devrait faire sortir les cheveux de tout programmeur Java de l'horreur: en C ++, le moyen le plus orienté objet d'ajouter une fonctionnalité à un objet est via une fonction non-membre non-amie, au lieu d'un membre- fonction (c'est-à-dire méthode de classe), car:
En C ++, l'interface d'une classe est à la fois ses fonctions membres et les fonctions non membres dans le même espace de noms
les fonctions non-amies non-membres n'ont pas d'accès privilégié à la classe interne. En tant que tel, l'utilisation d'une fonction membre sur une fonction non-amie non-membre affaiblira l'encapsulation de la classe.
Cela ne manque jamais de surprendre même les développeurs expérimentés.
(Source: Entre autres, le gourou en ligne de Herb Sutter # 84: http://www.gotw.ca/gotw/084.htm )
la source
Une caractéristique du langage que je considère comme quelque peu cachée, car je n'en avais jamais entendu parler pendant tout mon séjour à l'école, est l'alias d'espace de noms. Cela n'a pas été porté à mon attention jusqu'à ce que j'en trouve des exemples dans la documentation du boost. Bien sûr, maintenant que je le connais, vous pouvez le trouver dans n'importe quelle référence C ++ standard.
la source
using
.Non seulement les variables peuvent être déclarées dans la partie init d'une
for
boucle, mais aussi les classes et les fonctions.Cela permet plusieurs variables de types différents.
la source
L'opérateur de tableau est associatif.
A [8] est un synonyme de * (A + 8). Puisque l'addition est associative, cela peut être réécrit comme * (8 + A), qui est synonyme de ..... 8 [A]
Vous n'avez pas dit utile ... :-)
la source
A
n'importe pas du tout. Par exemple, siA
était achar*
, le code serait toujours valide.Une chose que l'on sait peu est que les unions peuvent aussi être des modèles:
Et ils peuvent aussi avoir des constructeurs et des fonctions membres. Rien qui ait à voir avec l'héritage (y compris les fonctions virtuelles).
la source
From
etTo
sont définis et utilisés en conséquence. Une telle union peut être utilisée avec un comportement défini (To
étant un tableau de caractères non signés ou une structure partageant une séquence initiale avecFrom
). Même si vous l'utilisez d'une manière non définie, cela peut toujours être utile pour les travaux de bas niveau. Quoi qu'il en soit, ce n'est qu'un exemple d'un modèle d'union - il peut y avoir d'autres utilisations pour une union basée sur un modèle.C ++ est un langage multi-paradigme, vous pouvez parier votre dernier argent sur l'existence de fonctionnalités cachées. Un exemple parmi tant d'autres: la métaprogrammation de modèles . Personne dans le comité de normalisation n'avait l'intention qu'il y ait un sous-langage complet de Turing qui soit exécuté au moment de la compilation.
la source
Une autre fonctionnalité cachée qui ne fonctionne pas en C est la fonctionnalité de l'
+
opérateur unaire . Vous pouvez l'utiliser pour promouvoir et décomposer toutes sortes de chosesConversion d'une énumération en un entier
Et votre valeur d'énumération qui avait auparavant son type d'énumération a maintenant le type entier parfait qui peut correspondre à sa valeur. Manuellement, vous ne sauriez guère ce type! Cela est nécessaire, par exemple, lorsque vous souhaitez implémenter un opérateur surchargé pour votre énumération.
Obtenir la valeur d'une variable
Vous devez utiliser une classe qui utilise un initialiseur statique en classe sans définition hors classe, mais parfois il échoue à se lier? L'opérateur peut aider à créer un temporaire sans faire d'hypothèses ou de dépendances sur son type
Décomposition d'un tableau en pointeur
Voulez-vous passer deux pointeurs à une fonction, mais cela ne fonctionnera tout simplement pas? L'opérateur peut aider
la source
La durée de vie des temporaires liées aux références const est celle que peu de gens connaissent. Ou du moins, c'est ma connaissance C ++ préférée que la plupart des gens ne connaissent pas.
la source
Une fonctionnalité intéressante qui n'est pas souvent utilisée est le bloc try-catch à l'échelle de la fonction:
L'utilisation principale serait de traduire l'exception en une autre classe d'exception et de la renvoyer, ou de traduire entre les exceptions et la gestion des codes d'erreur basée sur les retours.
la source
return
partir du bloc catch de Function Try, mais uniquement de nouveau.Beaucoup connaissent la métafonction
identity
/id
, mais il y a un bon cas d'utilisation pour les cas non-template: Facilité d'écriture des déclarations:Cela aide grandement à déchiffrer les déclarations C ++!
la source
template<typename Ret,typename... Args> using function = Ret (Args...); template<typename T> using pointer = *T;
->pointer<function<void,int>> f(pointer<function<void,void>>);
orpointer<void(int)> f(pointer<void()>);
orfunction<pointer<function<void,int>>,pointer<function<void,void>>> f;
Une fonctionnalité assez cachée est que vous pouvez définir des variables dans une condition if, et sa portée ne s'étendra que sur les blocs if et else:
Certaines macros l'utilisent, par exemple pour fournir une portée "verrouillée" comme celle-ci:
BOOST_FOREACH l'utilise également sous le capot. Pour compléter cela, ce n'est pas seulement possible dans un if, mais aussi dans un switch:
et dans une boucle while:
(et aussi en condition pour). Mais je ne sais pas trop si tout cela est utile :)
la source
if((a = f()) == b) ...
, mais cette réponse déclare en fait une variable dans la condition.for(...; int i = foo(); ) ...;
Cela traversera le corps aussi longtemps quei
c'est vrai, en l'initialisant à chaque fois. La boucle que vous montrez montre simplement une déclaration de variable, mais pas une déclaration de variable qui agit simultanément comme une condition :)Empêcher l'opérateur de virgule d'appeler les surcharges d'opérateur
Parfois, vous utilisez correctement l'opérateur virgule, mais vous voulez vous assurer qu'aucun opérateur virgule défini par l'utilisateur ne vous gêne, car par exemple, vous vous fiez aux points de séquence entre le côté gauche et droit ou vous voulez vous assurer que rien n'interfère avec le action. C'est là
void()
qu'entre en jeu:Ignorez les espaces réservés que j'ai mis pour la condition et le code. L'important est le
void()
, qui oblige le compilateur à utiliser l'opérateur virgule intégré. Cela peut être utile lors de l'implémentation de classes de traits, parfois aussi.la source
Initialisation du tableau dans le constructeur. Par exemple dans une classe si nous avons un tableau de
int
as:Nous pouvons initialiser tous les éléments du tableau à sa valeur par défaut (ici tous les éléments du tableau à zéro) dans le constructeur comme:
la source
Oooh, je peux plutôt proposer une liste de détestations pour les animaux:
Du coté positif
la source
Vous pouvez accéder aux données protégées et aux membres de fonction de n'importe quelle classe, sans comportement indéfini et avec la sémantique attendue. Lisez la suite pour voir comment. Lisez également le rapport de défaut à ce sujet.
Normalement, C ++ vous interdit d'accéder aux membres protégés non statiques de l'objet d'une classe, même si cette classe est votre classe de base
C'est interdit: vous et le compilateur ne savez pas sur quoi la référence pointe réellement. Il peut s'agir d'un
C
objet, auquel cas la classeB
n'a aucune activité et aucune idée de ses données. Un tel accès n'est accordé que s'ilx
s'agit d'une référence à une classe dérivée ou à une classe dérivée de celle-ci. Et cela pourrait permettre à un morceau de code arbitraire de lire n'importe quel membre protégé en créant simplement une classe "à jeter" qui lit les membres, par exemplestd::stack
:Comme vous le voyez, cela causerait bien trop de dégâts. Mais maintenant, les pointeurs membres permettent de contourner cette protection! Le point clé est que le type d'un pointeur de membre est lié à la classe qui contient réellement ledit membre - pas à la classe que vous avez spécifiée lors de la prise de l'adresse. Cela nous permet de contourner la vérification
Et bien sûr, cela fonctionne également avec l'
std::stack
exemple.Cela sera encore plus facile avec une déclaration using dans la classe dérivée, qui rend le nom du membre public et fait référence au membre de la classe de base.
la source
Une autre fonctionnalité cachée est que vous pouvez appeler des objets de classe qui peuvent être convertis en pointeurs de fonction ou en références. La résolution des surcharges se fait sur leur résultat et les arguments sont parfaitement transmis.
Celles-ci sont appelées "fonctions d'appel de substitution".
la source
Fonctionnalités cachées:
Si une fonction lève une exception non répertoriée dans ses spécifications d'exception, mais que la fonction l'a
std::bad_exception
dans sa spécification d'exception, l'exception est convertiestd::bad_exception
et levée automatiquement. De cette façon, vous saurez au moins qu'un abad_exception
été lancé. En savoir plus ici .blocs d'essai de fonction
Le mot-clé template pour lever l'ambiguïté des typedefs dans un modèle de classe. Si le nom d'une spécialisation de modèle de membre apparaît après
.
,->
ou::
opérateur, et ce nom a des paramètres de modèle explicitement qualifiés, préfixe le nom du modèle de membre avec le modèle mot - clé. En savoir plus ici .les valeurs par défaut des paramètres de fonction peuvent être modifiées lors de l'exécution. En savoir plus ici .
A[i]
fonctionne aussi bien quei[A]
Les instances temporaires d'une classe peuvent être modifiées! Une fonction membre non-const peut être appelée sur un objet temporaire. Par exemple:
En savoir plus ici .
Si deux types différents sont présents avant et après le
:
dans l'?:
expression de l'opérateur ternary ( ), le type résultant de l'expression est celui qui est le plus général des deux. Par exemple:la source
map::operator[]
crée une entrée si la clé est manquante et renvoie une référence à la valeur d'entrée construite par défaut. Vous pouvez donc écrire:Je suis étonné de voir combien de programmeurs C ++ ne le savent pas.
la source
.find()
.const map::operator[]
génère des messages d'erreur"Le fait de placer des fonctions ou des variables dans un espace de noms sans nom rend obsolète l'utilisation de
static
pour les limiter à l'étendue du fichier.la source
static
la portée mondiale n'est en aucun cas obsolète. (Pour référence: C ++ 03 §D.2)static
use ne doit être utilisé que dans un type de classe ou une fonction.La définition des fonctions d'amis ordinaires dans les modèles de classe nécessite une attention particulière:
Dans cet exemple, deux instanciations différentes créent deux définitions identiques - une violation directe de l' ODR
Nous devons donc nous assurer que les paramètres du modèle du modèle de classe apparaissent dans le type de n'importe quelle fonction amie définie dans ce modèle (à moins que nous ne voulions empêcher plus d'une instanciation d'un modèle de classe dans un fichier particulier, mais c'est plutôt improbable). Appliquons ceci à une variante de notre exemple précédent:
Clause de non-responsabilité: J'ai collé cette section à partir de modèles C ++: Le guide complet / Section 8.4
la source
les fonctions void peuvent renvoyer des valeurs void
Peu connu, mais le code suivant convient
Aussi étrange que le suivant
Sachant cela, vous pouvez en profiter dans certains domaines. Un exemple: les
void
fonctions ne peuvent pas renvoyer une valeur, mais vous pouvez également ne pas simplement renvoyer rien, car elles peuvent être instanciées avec non-void. Au lieu de stocker la valeur dans une variable locale, ce qui provoquera une erreur pourvoid
, renvoyez simplement une valeur directementla source
Lire un fichier dans un vecteur de chaînes:
istream_iterator
la source
vector<string> V((istream_iterator<string>(cin)), istream_iterator<string>());
- parenthèses manquantes après le deuxièmeVous pouvez modéliser des champs de bits.
Je n'ai pas encore trouvé d'objectif pour cela, mais cela m'a vraiment surpris.
la source
L'une des grammaires les plus intéressantes de tous les langages de programmation.
Trois de ces choses vont ensemble, et deux sont quelque chose de complètement différent ...
Tous sauf le troisième et le cinquième définissent un
SomeType
objet sur la pile et l'initialisent (avecu
dans les deux premiers cas, et le constructeur par défaut dans le quatrième. Le troisième déclare une fonction qui ne prend aucun paramètre et renvoie aSomeType
. Le cinquième déclare de la même manière une fonction qui prend un paramètre par valeur de typeSomeType
nomméu
.la source
Se débarrasser des déclarations anticipées:
Écrire des instructions de commutation avec les opérateurs?::
Tout faire sur une seule ligne:
Remise à zéro des structures sans memset:
Normalisation / enroulement des valeurs d'angle et de temps:
Attribution de références:
la source
FStruct s = {};
est encore plus court.main
? Je suggèreglobal().main();
et simplement oublier le singleton ( vous pouvez simplement travailler avec le temporaire, qui obtient la vie de ce étendu )L'opérateur conditionnel ternaire
?:
exige que ses deuxième et troisième opérandes aient des types "agréables" (parlant de manière informelle). Mais cette exigence a une exception (jeu de mots): le deuxième ou le troisième opérande peut être une expression throw (qui a un typevoid
), quel que soit le type de l'autre opérande.En d'autres termes, on peut écrire les expressions C ++ parfaitement valides suivantes en utilisant l'
?:
opérateurBTW, le fait que l'expression throw soit en fait une expression (de type
void
) et non une instruction est une autre caractéristique peu connue du langage C ++. Cela signifie, entre autres, que le code suivant est parfaitement validebien qu'il n'y ait pas grand chose à faire de cette façon (peut-être que dans un code de modèle générique, cela pourrait être utile).
la source
La règle de dominance est utile, mais peu connue. Il dit que même si dans un chemin non unique à travers un treillis de classe de base, la recherche de nom pour un membre partiellement caché est unique si le membre appartient à une classe de base virtuelle:
J'ai utilisé cela pour implémenter un support d'alignement qui détermine automatiquement l'alignement le plus strict au moyen de la règle de dominance.
Cela ne s'applique pas seulement aux fonctions virtuelles, mais également aux noms de typedef, aux membres statiques / non virtuels et à toute autre chose. Je l'ai vu utilisé pour implémenter des traits écrasables dans les méta-programmes.
la source
struct C
dans votre exemple ...? À votre santé.