Les caractéristiques cachées du c++? [fermé]

Pas de C++ l'amour quand il s'agit de la "fonctionnalités cachées de la" ligne de questions? Pensé que je pourrais le jeter là-bas. Quelles sont les fonctionnalités cachées de C++?

114
demandé sur Craig H 2008-09-16 22:37:05
la source

30 ответов

la plupart des programmeurs C++ sont familiers avec l'opérateur ternaire:

x = (y < 0) ? 10 : 20;

cependant, ils ne se rendent pas compte qu'il peut être utilisé comme une valeur de l:

(a == 0 ? a : b) = 1;

qui est abréviation de

if (a == 0)
    a = 1;
else
    b = 1;

utiliser avec prudence: -)

308
répondu Ferruccio 2009-01-08 00:59:34
la source

vous pouvez mettre URIs dans C++ source sans erreur. Par exemple:

void foo() {
    http://stackoverflow.com/
    int bar = 4;

    ...
}
238
répondu Ben 2008-09-17 05:09:32
la source

Pointeur de l'arithmétique.

Les programmeurs

C++ préfèrent éviter les pointeurs à cause des bugs qui peuvent être introduits.

le c++ le plus cool que j'ai jamais vu? Analogique littéraux.

140
répondu 3 revs, 3 users 88%Anonymouse 2013-03-07 16:40:11
la source

je suis d'accord avec la plupart des messages: C++ est un langage multi-paradigme, donc les fonctionnalités" cachées "que vous trouverez (autres que les" comportements non définis " que vous devriez éviter à tout prix) sont des utilisations intelligentes des installations.

la Plupart de ces installations ne sont pas des fonctionnalités intégrées de la langue, mais de la bibliothèque.

le plus important est le RAII , souvent ignoré pendant des années par les développeurs C++ venant du monde C. " Operator overloading " " est souvent une caractéristique mal comprise qui permet à la fois un comportement de type tableau (opérateur subscript), des opérations de type pointeur (pointeurs intelligents) et des opérations de type build-in (matrices multiplicatrices).

l'utilisation de exception est souvent difficile, mais avec un certain travail, peut produire un code vraiment robuste à travers sécurité d'exception spécifications( y compris le code qui ne manquera pas, ou qui aura un s'engager comme caractéristiques qui est la volonté de réussir, ou de revenir à son état d'origine).

le plus célèbre de la fonctionnalité" cachée "de C++ est template metaprogramming , car il vous permet d'avoir votre programme partiellement (ou totalement) exécuté à la compilation au lieu de l'exécuter. C'est difficile, cependant, et vous devez avoir une connaissance solide sur les modèles avant de l'essayer.

autres faire usage du paradigme multiple pour produire " des moyens de programmation "en dehors de l'ancêtre de C++, c'est-à-dire, C.

en utilisant functors , vous pouvez simuler des fonctions, avec le type supplémentaire-sécurité et stateful. En utilisant la commande , vous pouvez retarder l'exécution du code. La plupart des autres design patterns 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 c++".

en utilisant templates , vous pouvez produire du code qui fonctionnera sur la plupart des types, y compris pas celui que vous pensiez au début. Vous pouvez augmenter la sécurité de type, aussi (comme une sécurité de type automatique malloc/realloc/gratuit). Les caractéristiques de l'objet C++ sont vraiment puissantes (et donc dangereuses si utilisées négligemment), mais même le polymorphisme dynamique a sa version statique en C++: le CRTP .

j'ai trouvé que la plupart " C++ efficace "- type livres de Scott Meyers ou " C exceptionnel++ " - type livres de Herb Sutter pour être à la fois facile à lire, et tout à fait trésors d'information sur les caractéristiques connues et moins connues de C++.

parmi mes préférés, il y en a un qui devrait faire monter les cheveux de n'importe quel programmeur Java de l'horreur: en C++, la façon la plus orientée objet d'ajouter une fonctionnalité à un objet est par le biais d'un non-membre non-ami fonction, au lieu d'une fonction de membre (c.-à-d. méthode de classe), parce que:

  • dans C++, une interface de classe est à la fois ses fonctions membres et les fonctions non-membres dans le même espace de noms

  • non-ami non membre les fonctions n'ont pas un accès privilégié à la classe interne. Ainsi, l'utilisation d'une fonction de membre sur un non-membre non-ami affaiblira l'encapsulation de la classe.

cela ne manque jamais de surprendre même les développeurs expérimentés.

(Source: Entre autres, Herb Sutter en ligne le Gourou de la Semaine n ° 84: http://www.gotw.ca/gotw/084.htm )

119
répondu paercebal 2010-05-29 01:25:19
la source

une caractéristique de langue que je considère comme quelque peu cachée, parce que je n'avais jamais entendu parler de lui tout au long de mon temps à l'école, est le namespace alias. Il n'a pas été porté à mon attention jusqu'à ce que j'en trouve des exemples dans la documentation de boost. Bien sûr, maintenant que je le sais, Vous pouvez le trouver dans n'importe quelle référence C++ standard.

namespace fs = boost::filesystem;

fs::path myPath( strPath, fs::native );
118
répondu Jason Mock 2008-09-17 03:57:50
la source

non seulement les variables peuvent être déclarées dans la partie init d'une boucle for , mais aussi les classes et les fonctions.

for(struct { int a; float b; } loop = { 1, 2 }; ...; ...) {
    ...
}

qui permet des variables multiples de types différents.

102
répondu Johannes Schaub - litb 2009-05-20 20:36:38
la source

l'opérateur array est associatif.

A[8] est synonyme de *(A + 8). Puisque l'addition est associative, cela peut être réécrit comme *(8 + A), qui est un synonyme pour..... 8 [A]

Vous n'avez pas dit utile... :- )

77
répondu Colin Jensen 2008-09-17 00:41:23
la source

une chose qui est peu connue est que les syndicats peuvent être des modèles aussi:

template<typename From, typename To>
union union_cast {
    From from;
    To   to;

    union_cast(From from)
        :from(from) { }

    To getTo() const { return to; }
};

et ils peuvent aussi avoir des fonctions de constructeur et de membre. Juste rien qui a à voir avec l'héritage (y compris les fonctions virtuelles).

73
répondu Johannes Schaub - litb 2009-01-22 14:22:00
la source

C++ est un standard, il ne devrait pas y avoir de fonctionnalités cachées...

C++ est un langage multi-paradigme, vous pouvez parier votre dernier argent sur les fonctionnalités cachées. Un exemple parmi tant d'autres: modèle de la métaprogrammation . Aucun membre du comité de normalisation n'avait l'intention d'utiliser une sous-langue Turing-complete qui serait exécutée au moment de la compilation.

72
répondu Konrad Rudolph 2008-09-16 22:46:07
la source

une autre fonctionnalité cachée qui ne fonctionne pas en C est la fonctionnalité de l'opérateur unary + . Vous pouvez l'utiliser pour promouvoir et pourrir toutes sortes de choses

conversion D'une énumération en un entier

+AnEnumeratorValue

et votre valeur d'énumérateur qui avait auparavant son type d'énumération a maintenant le type entier parfait qui peut correspondre à sa valeur. Manuellement, vous ne connaîtriez pas ce type! Cela est nécessaire, par exemple lorsque vous voulez mettre en place un opérateur surchargé pour votre recensement.

Obtenir la valeur d'une variable

vous devez utiliser une classe qui utilise un initialiseur statique de classe sans définition hors classe, mais parfois il ne parvient pas à se lier? L'opérateur peut aider à créer un temporaire sans faire des présomptions ou des dépendances sur son type

struct Foo {
  static int const value = 42;
};

// This does something interesting...
template<typename T>
void f(T const&);

int main() {
  // fails to link - tries to get the address of "Foo::value"!
  f(Foo::value);

  // works - pass a temporary value
  f(+Foo::value);
}

Désintégration d'un tableau à un pointeur

voulez-vous passer deux pointer vers une fonction, mais ça ne marchera pas? L'opérateur peut aider

// This does something interesting...
template<typename T>
void f(T const& a, T const& b);

int main() {
  int a[2];
  int b[3];
  f(a, b); // won't work! different values for "T"!
  f(+a, +b); // works! T is "int*" both time
}
66
répondu Johannes Schaub - litb 2010-07-06 03:00:13
la source

durée de Vie temporaires liés à const références, c'est que peu de gens connaissent. Ou du moins c'est mon morceau préféré de connaissance C++ que la plupart des gens ne connaissent pas.

const MyClass& x = MyClass(); // temporary exists as long as x is in scope
61
répondu MSN 2010-07-06 03:07:54
la source

une belle caractéristique qui n'est pas souvent utilisé est la fonction à l'échelle du bloc try-catch:

int Function()
try
{
   // do something here
   return 42;
}
catch(...)
{
   return -1;
}

L'usage principal serait de traduire l'exception à une autre classe d'exception et à rethrow, ou de traduire entre les exceptions et le traitement de code d'erreur basé sur le retour.

52
répondu vividos 2008-09-30 15:36:44
la source

beaucoup de gens connaissent la identity / id metafunction, mais il y a une bonne usecase pour cela pour les cas non-template: Ease writing declarations:

// void (*f)(); // same
id<void()>::type *f;

// void (*f(void(*p)()))(int); // same
id<void(int)>::type *f(id<void()>::type *p);

// int (*p)[2] = new int[10][2]; // same
id<int[2]>::type *p = new int[10][2];

// void (C::*p)(int) = 0; // same
id<void(int)>::type C::*p = 0;

cela aide grandement à déchiffrer les déclarations C++!

// boost::identity is pretty much the same
template<typename T> 
struct id { typedef T type; };
44
répondu Johannes Schaub - litb 2009-09-12 15:06:19
la source

une caractéristique assez cachée est que vous pouvez définir des variables dans une condition if, et son champ d'application ne s'étendra que sur le SI, et ses autres blocs:

if(int * p = getPointer()) {
    // do something
}

certaines macros utilisent cela, par exemple pour fournir une portée "verrouillée" comme ceci:

struct MutexLocker { 
    MutexLocker(Mutex&);
    ~MutexLocker(); 
    operator bool() const { return false; } 
private:
    Mutex &m;
};

#define locked(mutex) if(MutexLocker const& lock = MutexLocker(mutex)) {} else 

void someCriticalPath() {
    locked(myLocker) { /* ... */ }
}

BOOST_FOREACH l'utilise aussi sous le capot. Pour compléter cela, il n'est pas seulement possible dans un si, mais aussi dans un commutateur:

switch(int value = getIt()) {
    // ...
}

et dans une boucle de temps:

while(SomeThing t = getSomeThing()) {
    // ...
}

(et aussi dans le cadre d'une condition). Mais je ne suis pas trop sûr si ceux-ci sont tout ce que utile:)

43
répondu Johannes Schaub - litb 2009-02-21 16:56:56
la source

empêcher l'opérateur de virgule d'appeler des surcharges d'opérateur

parfois vous faites une utilisation valide de l'opérateur virgule, Mais vous voulez vous assurer qu'aucun opérateur virgule défini par l'utilisateur ne se met en travers, parce que par exemple vous vous fiez sur les points de séquence entre le côté gauche et le côté droit ou que vous voulez vous assurer que rien n'interfère avec l'action désirée. C'est là que void() entre en jeu:

for(T i, j; can_continue(i, j); ++i, void(), ++j)
  do_code(i, j);

ignorez les supports de place que j'ai mis pour la condition et le code. Ce qui est important , c'est le void() , qui rend la force du compilateur pour utiliser l'opérateur de virgule intégré. Cela peut être utile lors de la mise en œuvre de classes de traits, parfois, aussi.

29
répondu Johannes Schaub - litb 2011-02-04 08:26:07
la source

initialisation du tableau dans le constructeur. Par exemple, dans une classe, si nous avons un tableau de int :

class clName
{
  clName();
  int a[10];
};

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:

clName::clName() : a()
{
}
28
répondu Sirish 2009-09-23 16:41:47
la source

Oooh, je peux venir avec une liste d'animaux de compagnie déteste à la place:

  • les destructeurs doivent être virtuels si vous avez l'intention d'utiliser polymorphically
  • parfois les membres sont initialisés par défaut, parfois ils ne le sont pas
  • clases locales ne peuvent pas être utilisées comme paramètres de modèle (les rend moins utiles)
  • exception des prescripteurs: regardez utiles, mais ne sont pas
  • les surcharges de la fonction hide base fonctions de classe avec différentes signatures.
  • pas utile de normalisation sur l'internationalisation (portable standard large charset, n'importe qui? Nous devrons attendre Jusqu'à C++0x)

Sur le côté plus

  • fonctionnalité cachée: la fonction des blocs try. Malheureusement, je n'ai pas trouvé une utilisation pour elle. Oui, je sais pourquoi ils l'ont ajouté, mais il faut repenser un constructeur qui le rend inutile.
  • c'est il vaut la peine d'examiner attentivement les garanties STL sur la validité iterator après modification du conteneur, qui peut vous permettre de faire des boucles légèrement plus belles.
  • Boost-ce n'est pas un secret mais ça en vaut la peine.
  • optimisation de la valeur de retour (pas évident, mais c'est spécifiquement autorisé par la norme)
  • Foncteurs aka fonction des objets aka opérateur(). Ce système est largement utilisé par le STL. pas vraiment un secret, mais c'est un chouette effet secondaire de la surcharge de l'opérateur et des gabarits.
27
répondu Robert 2008-09-17 04:06:56
la source

vous pouvez accéder aux données protégées et aux membres de fonction de n'importe quelle classe, sans comportement non défini, et avec la sémantique attendue. Lire la suite pour voir comment. Lire aussi 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

struct A {
protected:
    int a;
};

struct B : A {
    // error: can't access protected member
    static int get(A &x) { return x.a; }
};

struct C : A { };

c'est interdit: vous et le compilateur ne savez pas ce que la référence en fait des points. Il peut s'agir d'un objet C , auquel cas la classe B n'a pas d'activité ni d'indice sur ses données. Cet accès n'est accordé que si x fait référence à une classe dérivée ou à une classe dérivée. Et elle pourrait permettre à un morceau de code arbitraire de lire n'importe quel membre protégé en créant simplement une classe "jeter-away" qui lit les membres, par exemple de std::stack :

void f(std::stack<int> &s) {
    // now, let's decide to mess with that stack!
    struct pillager : std::stack<int> {
        static std::deque<int> &get(std::stack<int> &s) {
            // error: stack<int>::c is protected
            return s.c;
        }
    };

    // haha, now let's inspect the stack's middle elements!
    std::deque<int> &d = pillager::get(s);
}

sûrement, comme vous voyez cela causerait beaucoup trop dommage. Mais maintenant, les pointeurs de membre 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 - et non à la classe que vous avez spécifiée en prenant l'adresse. Cela nous permet de contourner le contrôle

struct A {
protected:
    int a;
};

struct B : A {
    // valid: *can* access protected member
    static int get(A &x) { return x.*(&B::a); }
};

struct C : A { };

et bien sûr, il fonctionne aussi avec l'exemple std::stack .

void f(std::stack<int> &s) {
    // now, let's decide to mess with that stack!
    struct pillager : std::stack<int> {
        static std::deque<int> &get(std::stack<int> &s) {
            return s.*(pillager::c);
        }
    };

    // haha, now let's inspect the stack's middle elements!
    std::deque<int> &d = pillager::get(s);
}

ce sera encore plus facile avec un en utilisant la déclaration dans la classe dérivée, qui rend le nom du membre public et se réfère au membre de la classe de base.

void f(std::stack<int> &s) {
    // now, let's decide to mess with that stack!
    struct pillager : std::stack<int> {
        using std::stack<int>::c;
    };

    // haha, now let's inspect the stack's middle elements!
    std::deque<int> &d = s.*(&pillager::c);
}
27
répondu Johannes Schaub - litb 2009-09-10 02:14:48
la source

une autre caractéristique cachée est que vous pouvez appeler des objets de classe qui peuvent être convertis en pointeurs ou références de fonction. La résolution de surcharge se fait sur le résultat d'eux, et les arguments sont parfaitement transmis.

template<typename Func1, typename Func2>
class callable {
  Func1 *m_f1;
  Func2 *m_f2;

public:
  callable(Func1 *f1, Func2 *f2):m_f1(f1), m_f2(f2) { }
  operator Func1*() { return m_f1; }
  operator Func2*() { return m_f2; }
};

void foo(int i) { std::cout << "foo: " << i << std::endl; }
void bar(long il) { std::cout << "bar: " << il << std::endl; }

int main() {
  callable<void(int), void(long)> c(foo, bar);
  c(42); // calls foo
  c(42L); // calls bar
}

ces fonctions sont appelées "fonctions d'appel de substitution".

26
répondu Johannes Schaub - litb 2010-07-05 04:05:26
la source
"1519120920 des fonctionnalités Cachées:

  1. les fonctions virtuelles pures peuvent être implémentées. Exemple courant, destructeur virtuel pur.
  2. si une fonction lance une exception non listée dans ses spécifications d'exception, mais que la fonction a std::bad_exception dans ses spécifications d'exception, l'exception est convertie en std::bad_exception et lancée automatiquement. De cette façon, vous saurez au moins qu'un bad_exception a été lancé. Lire plus ici .

  3. la fonction des blocs try

  4. le mot-clé template dans les typographies ambiguës dans un template de classe. Si le nom d'un membre template specialization apparaît après un opérateur . , -> , ou :: , et que ce nom a explicitement qualifié les paramètres de template, préfixer le nom du membre template avec le mot-clé template. Lire la suite ici .

  5. les paramètres par défaut de la fonction peuvent être changés à l'exécution. Lire la suite ici .

  6. A[i] fonctionne aussi bien que i[A]

  7. les instances temporaires d'une classe peuvent être modifiées! Une fonction de membre non const peut être invoquée sur un objet temporaire. Par exemple:

    struct Bar {
      void modify() {}
    }
    int main (void) {
      Bar().modify();   /* non-const function invoked on a temporary. */
    }
    

    Lire la suite ici .

  8. si deux types différents sont présents avant et après : dans l'expression de l'opérateur ternaire ( ?: ), alors le type résultant de l'expression est celui qui est le plus général des deux. Par exemple:

    void foo (int) {}
    void foo (double) {}
    struct X {
      X (double d = 0.0) {}
    };
    void foo (X) {} 
    
    int main(void) {
      int i = 1;
      foo(i ? 0 : 0.0); // calls foo(double)
      X x;
      foo(i ? 0.0 : x);  // calls foo(X)
    }
    
26
répondu Sumant 2011-11-01 19:19:14
la source

map::operator[] crée l'entrée si la clé est manquante et renvoie la référence à la valeur d'entrée construite par défaut. Vous pouvez donc écrire:

map<int, string> m;
string& s = m[42]; // no need for map::find()
if (s.empty()) { // assuming we never store empty values in m
  s.assign(...);
}
cout << s;

je suis étonné de voir combien de programmeurs C++ ne savent pas cela.

24
répondu Constantin 2008-10-05 21:55:44
la source

Mettre des fonctions ou des variables dans un nom d'espace de noms dénonçait l'utilisation de static pour restreindre la portée de fichier.

20
répondu Jim Hunziker 2008-10-20 16:45:47
la source

définir les fonctions d'ami ordinaire dans les gabarits de classe nécessite une attention particulière:

template <typename T> 
class Creator { 
    friend void appear() {  // a new function ::appear(), but it doesn't 
        …                   // exist until Creator is instantiated 
    } 
};
Creator<void> miracle;  // ::appear() is created at this point 
Creator<double> oops;   // ERROR: ::appear() is created a second time! 

dans cet exemple, deux instanciations différentes créent deux définitions identiques-une violation directe du ODR

nous devons donc nous assurer que les paramètres de template du template de classe apparaissent dans le type de toute fonction ami définie dans ce template (à 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 rare). Appliquons ceci à une variante de notre exemple précédent:

template <typename T> 
class Creator { 
    friend void feed(Creator<T>*){  // every T generates a different 
        …                           // function ::feed() 
    } 
}; 

Creator<void> one;     // generates ::feed(Creator<void>*) 
Creator<double> two;   // generates ::feed(Creator<double>*) 

avertissement: j'ai collé cette section de modèles C++: le Guide complet / Section 8.4

19
répondu Özgür 2009-01-19 10:05:32
la source

les fonctions de vide peuvent retourner les valeurs de vide

peu connu, mais le code suivant est amende

void f() { }
void g() { return f(); }

ainsi que l'étrange suivant

void f() { return (void)"i'm discarded"; }

sachant cela, vous pouvez en profiter dans certains domaines. Un exemple: les fonctions void ne peuvent pas retourner une valeur mais vous ne pouvez pas non plus simplement retourner rien, car elles peuvent être instanciées avec non-void. Au lieu de stocker la valeur dans un local variable, qui causera une erreur pour void , il suffit de retourner une valeur directement

template<typename T>
struct sample {
  // assume f<T> may return void
  T dosomething() { return f<T>(); }

  // better than T t = f<T>(); /* ... */ return t; !
};
18
répondu Johannes Schaub - litb 2010-02-01 14:15:50
la source

lire un fichier dans un vecteur de chaînes:

 vector<string> V;
 copy(istream_iterator<string>(cin), istream_iterator<string>(),
     back_inserter(V));

istream_iterator

17
répondu Jason Baker 2008-11-20 05:40:27
la source

vous pouvez template bitfields.

template <size_t X, size_t Y>
struct bitfield
{
    char left  : X;
    char right : Y;
};

Je n'ai pas encore trouvé de raison pour cela, mais ça m'a vraiment surpris.

14
répondu Kaz Dragon 2009-11-20 20:01:16
la source

un des grammaires les plus intéressants de tous les langages de programmation.

trois de ces choses appartiennent ensemble, et deux sont quelque chose de tout à fait différent...

SomeType t = u;
SomeType t(u);
SomeType t();
SomeType t;
SomeType t(SomeType(u));

tous sauf le troisième et le cinquième définissent un SomeType objet sur la pile et l'initialisent (avec u dans les deux premiers cas, et le constructeur par défaut dans le quatrième. La troisième est de déclarer une fonction qui ne prend aucun paramètre et renvoie un SomeType . Le cinquième est de même en déclarant une fonction qui prend un paramètre par valeur de type SomeType nommé u .

14
répondu Eclipse 2010-02-26 11:47:33
la source

Se débarrasser de sa déclaration:

struct global
{
     void main()
     {
           a = 1;
           b();
     }
     int a;
     void b(){}
}
singleton;

l'Écriture de l'interrupteur-états ?: opérateurs:

string result = 
    a==0 ? "zero" :
    a==1 ? "one" :
    a==2 ? "two" :
    0;

tout Faire sur une seule ligne:

void a();
int b();
float c = (a(),b(),1.0f);

mise à zéro des structures sans memset:

FStruct s = {0};

valeurs D'angle et de temps de normalisation/d'emballage:

int angle = (short)((+180+30)*65536/360) * 360/65536; //==-150

Affectation de références:

struct ref
{
   int& r;
   ref(int& r):r(r){}
};
int b;
ref a(b);
int c;
*(int**)&a = &c;
12
répondu AareP 2009-12-27 22:02:38
la source

L'opérateur conditionnel ternaire ?: exige de ses deuxième et troisième opérande d'avoir "agréables" types (en parlant de façon informelle). Mais cette exigence a une exception (jeu de mots prévu): soit le deuxième ou le troisième opérande peut être une expression de lancer (qui a le type void ), quel que soit le type de l'autre opérande.

en d'autres termes, on peut écrire les expressions c++ suivantes qui sont directement valides en utilisant l'opérateur ?: 151970920"

i = a > b ? a : throw something();

BTW, le fait que l'expression throw est en fait une expression (de type void ) et non une déclaration est une autre caractéristique peu connue du langage C++. Cela signifie, entre autres, que le code suivant est parfaitement valide

void foo()
{
  return throw something();
}

bien qu'il n'y ait pas beaucoup d'intérêt à le faire de cette façon (peut-être que dans un code de modèle générique cela pourrait être pratique).

12
répondu AnT 2010-02-26 10:08:10
la source

La domination de la règle est utile, mais peu connu. Il dit que même si dans un chemin non-unique à travers un réseau de classe de base, la recherche de nom pour un membre partiellement caché est unique si le membre appartient à une classe de base virtuelle:

struct A { void f() { } };

struct B : virtual A { void f() { cout << "B!"; } };
struct C : virtual A { };

// name-lookup sees B::f and A::f, but B::f dominates over A::f !
struct D : B, C { void g() { f(); } };

j'ai utilisé ceci pour implémenter alignement-support qui calcule automatiquement l'alignement le plus strict au moyen de la règle de dominance.

s'appliquer à des fonctions virtuelles, mais aussi pour typedef noms, statique/non-membres virtuels et rien d'autre. j'ai vu qu'il est utilisé pour implémenter des traits overwritable dans les méta-programmes.

12
répondu Johannes Schaub - litb 2017-05-23 15:34:59
la source

Autres questions sur c++ hidden-features