Quelle est la différence entre type et nom en C ++?

Je lis cette question de débordement de stack et j’ai ajouté un constructeur au code de cette question comme suit:

class Foo { struct Bar { int i; Bar(int a = 5) :i(a) {}; }; public: Bar Baz() { return Bar(); } }; int main() { Foo f; // Foo::Bar b = f.Baz(); // error auto b = f.Baz(); // ok std::cout <<"bi="<< bi<<endl; return 0; } 

Le code affiche bi=5 . Dans cette question, il conclut que le nom du privé n’est pas accessible mais que le type l’est. Alors, quelle est la différence entre le type et le nom, en général?

Et dis que j’ai deux scénarios spécifiques.

  1. Quelle est la différence entre les deux déclarations suivantes? Pourquoi puis-je obtenir la sortie bi=5 de auto b = f.Baz(); ?

     Foo::Bar b = f.Baz(); auto b = f.Baz(); 
  2. Si j’ajoute typedef Bar B; dans la partie publique de Foo , quelle est la différence entre ce qui suit?

      Foo::Bar b = f.Baz(); Foo::B b = f.Baz(); 

S’il y a une différence entre les scénarios 1 et 2?

Quelle est la difference entre type et name

Un type n’a aucun nom, un ou plusieurs noms. Typedef et alias sont simplement des moyens de créer un nouveau nom pour un type.

public mots clés public et private rapportent à des noms et non aux types ou membres sous-jacents.

Pour déclarer explicitement un object d’un type particulier, vous avez besoin d’un nom pour ce type. auto n’a pas besoin de ça. Si vous utilisez par exemple une classe non nommée comme type de retour , cette classe n’a pas de nom mais auto peut toujours être utilisée dessus.

Un type aura toujours au plus un ” vrai nom “. Même en l’utilisant via un typedef ou un alias, le compilateur l’utilise sous ce nom (ou en réalité la version brute de ce nom). Alors:

 class A {}; typedef AB; std::cout << typeid(B).name(); 

Imprime "classe A". Un object sans nom ne peut pas se voir atsortingbuer un «vrai nom». Cependant, lorsque vous utilisez typedef et decltype . Un nouveau nom peut être créé. Si ce nom est ensuite utilisé pour créer des objects. typeid().name affichera le nom nouvellement atsortingbué. Si l'object n'a pas été nommé, le nom du 'vrai nom' sera imprimé.


Les scénarios:

  1. La différence est que, dans le premier, vous utilisez un nom déclaré en privé. Ce qui est illégal. Cela a à voir avec le fonctionnement des différentes méthodes de déduction de type. Comme Scott Meyers explique ici . Etant donné qu'un appel de fonction public fournit ce type, le type de retour est public. Cependant, Bar sur lui-même n'est pas public. C'est une petite différence, mais c'est la raison.

    C'est simplement une décision qui a été prise dans la conception. Cela a du sens, parfois vous ne voulez utiliser une structure que lorsque vous la renvoyez.

  2. La même chose va ici. Il n'y a pas de différence, cependant Foo::Bar est tout simplement inaccessible.


modifier

Pouvez-vous donner un exemple que ce type n'a pas de nom? l'union sans nom dans le commentaire ci-dessus en est-elle un exemple?

Comme décrit ici, j'utilise une fonction lambda comme suit:

 auto f = [] () -> struct {int x, y ; } { return { 99, 101 } ; } ; 

Sans auto ou decltype, il n'y aurait aucun moyen de créer la variable f . Depuis son type n'a pas de nom. Un autre exemple d'un type sans nom.

 struct foo { struct{ int x; int y; } memberVar; }; 

Cela vous permettrait de faire ce qui suit:

 foo bar; auto baz = bar.memberVar; std::cout << baz.x; 

Ce qui résulte en un tas de choses initialisées bien sûr, mais vous avez l’idée :). Le type de memberVar ici n'est pas nommé. Il est impossible de définir explicitement baz .

Et int est-il considéré comme un nom pour type d'int?

int est un peu spécial, étant un type fondamental . 'int' est en effet le nom du type int. Mais ce n'est en aucun cas le seul nom, int32_t , par exemple, est un autre nom pour le même type exact sur la plupart des compilateurs (sur d'autres systèmes, int16_t est équivalent à int ).

 std::cout << typeid(int32_t).name(); 

Imprime "int".

Remarques:

  • Je me suis abstenu d'utiliser un alias comme indicateur pour d'autres noms d'object, car cela pourrait créer une confusion avec le mot clé alias.

  • La plupart de cela, je l'ai recueilli par expérience. Donc, j'ai peut-être manqué des morceaux

  • Par manque d'un meilleur mot, j'ai utilisé l'expression 'vrai nom'. Si quelqu'un connaît le responsable ou un meilleur mot pour cela, je serais heureux de l'entendre :).

[Certains standardese à venir]

Convenons que auto déduction auto fonctionne de la même manière que la déduction d’argument de modèle:

[dcl.spec.auto] / p7

Si l’espace réservé est le spécificateur de type automatique, le type déduit est déterminé à l’aide des règles de déduction d’argument de modèle.

Les modèles sont soumis à la recherche en deux phases lors de la compilation. Le contrôle d’access est appliqué à la recherche de nom dans la première phase

[basic.lookup] / p1

La résolution de surcharge (13.3) intervient une fois que la recherche de nom a réussi. Les règles d’access (clause 11) ne sont sockets en compte qu’une fois que la recherche de nom et la résolution de la surcharge de la fonction (le cas échéant) ont abouti. Ce n’est qu’après que la recherche de nom, la résolution de surcharge de la fonction (le cas échéant) et la vérification d’access ont réussi que les atsortingbuts introduits par la déclaration du nom soient utilisés ultérieurement dans le traitement de l’expression.

auto et decltype(auto) sont généralement des espaces réservés pour un type déduit, et il est vrai que [temp.arg] / p3 dit

Le nom d’un argument de modèle doit être accessible au point où il est utilisé comme argument de modèle.

mais les noms ne sont pas impliqués ici , seuls les types. Le contrôle d’access s’applique aux noms, un type peut être mappé sur 0, 1 ou plusieurs noms. C’est ce que vous devez faire lorsque vous utilisez auto dans le code ci-dessus.

[class.access] / p4

Le contrôle d’access est appliqué uniformément à tous les noms, qu’il s’agisse de déclarations ou d’expressions. […] L’accessibilité de l’entité référencée par le typedef n’est pas prise en compte. Par exemple

 class A { class B { }; public: typedef B BB; }; void f() { A::BB x; // OK, typedef name A::BB is public A::B y; // access error, A::B is private } 

Pour vous convaincre de ce qui suit, jetez un œil au même code avec déduction d’argument de gabarit (conceptuellement équivalent à la version auto ).

 template T deduce(T t) { return t; } class Foo { struct Bar{ int i; Bar(int a = 5):i(a){}; }; public: Bar *getB() { return new Bar(5); } // Leaks, doesn't matter for clarity's sake }; int main() { Foo f; std::cout <<"bi="<< deduce(f.getB())->i < 

Exemple en direct

Dans le code ci-dessus, les noms ne sont pas impliqués et le contrôle d'access ne s'applique donc pas. Les types sont en effet impliqués.

La sémantique de auto est semblable à la déduction implicite de modèles (le libellé normatif s'y réfère aussi directement).

Quelqu'un d'autre avait déjà eu ce doute .


Maintenant pour les réponses:

Case 1 est facile à accepter si vous considérez que l'appelant n'a pas access au nom Foo::Bar .

Case 2 expose également le nom à l'appelant. Par conséquent, si vous utilisez le nom typedef, votre code sera facilement compilé.

La question que vous liez en explique beaucoup, mais en complément de ce qui y est écrit …

  1. La différence principale est que dans la deuxième ligne, auto b=... vous laissez le compilateur déduire le type de l’expression. Vous ne pouvez pas spécifier le type, car le nom du type est masqué. Le type est utilisable (au moins depuis le compilateur)

  2. Vous exposez le nom du type publiquement afin qu’il puisse être utilisé.

C’est une très bonne réponse https://stackoverflow.com/a/13532882/3037915

Pour essayer de répondre à la question dans l’en-tête, vous pouvez considérer le type comme une forme et le nom du type comme le nom que vous utilisez pour faire référence à une forme particulière. Même si le nom de la “forme” est masqué, la forme existe toujours et peut être utilisée.