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

Je lis cette question de débordement de pile, et j'ai ajouté un constructeur au code de cette question en tant que follwing,

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 <<"b.i="<< b.i<<endl;
    return 0;
}

Le code sort b.i=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, généralement?

Et dire que j'ai deux scénarios spécifiques.

  1. Quelle est la différence entre les deux déclarations? Pourquoi puis-je obtenir la sortie b.i=5 à partir 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 la suivante?

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

S'il y a une différence entre le scénario 1 et 2?

22
demandé sur Community 2015-05-14 21:52:22

3 réponses

Quelle est la différence entre le type et le nom

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

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

, afin de déclarer explicitement un objet 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 sans nom 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 fait la version brute de ce nom). Alors:

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

Imprime "Classe A". Un objet sans nom ne peut pas recevoir de "vrai nom". Cependant, lorsque vous utilisez typedef et decltype. Un nouveau nom peut être créé. Si ce nom est alors utilisé pour créer des objets. {[10] } affichera le nouvel attribut nom. Si l'objet n'était pas sans nom pour commencer par le nom "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 la façon dont les différentes façons de travail de déduction de type. Comme L'explique Scott Meyersici . Comme un appel de fonction publique fournit ce type, le type de retour est public. Cependant, Bar sur lui-même n'est pas public. C'est un petit 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 voulez seulement qu'une structure soit utilisée lorsque vous la renvoyez.

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


Modifier

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

Tel Que décrit ici j'utilise une fonction lambda comme suit:

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

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

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

Vous permettrait de faire ce qui suit:

foo bar;

auto baz = bar.memberVar;

std::cout << baz.x;

Ce qui se traduit par un tas de choses initialisées bien sûr, mais vous avez l'idée:). Le type de memberVar ici n'est pas nommé. Rendre impossible la définition baz explicitement.

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

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

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

Imprime "int".

Notes:

  • Je me suis abstenu d'utiliser alias comme indicateur pour les autres noms d'un objet, car cela peut causer une confusion avec le mot-clé alias.

  • La Plupart de ce que j'ai recueilli de l'expérience. J'ai manqué quelques morceaux.

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

21
répondu laurisvr 2017-05-23 11:51:48

[Certains standardese à l'avance]

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

[dcl.specs.auto]/p7

Si l'espace réservé est l'auto spécificateur de type, 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'accès est appliqué à la recherche de nom dans le première phase

[de base.recherche]/p1

La résolution de surcharge (13.3) a lieu après que la recherche de nom a réussi. Les règles d'accès (Clause 11) ne sont prises en compte qu'une fois que la recherche de nom et la résolution de surcharge de fonction (le cas échéant) ont réussi. Ce n'est qu'après que la recherche de nom, la résolution de surcharge de fonction (le cas échéant) et la vérification d'accès ont réussi que les attributs introduits par la déclaration du nom sont utilisés plus loin dans expression traitement

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

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

Mais les noms ne sont pas impliqués ici , seulement les types. Le contrôle d'accès s'applique aux noms, un type peut être mappé à 0, 1 ou plusieurs noms et c'est ce que vous traitez lorsque vous utilisez auto dans le code ci-dessus: il est sémantiquement équivalent à la sémantique de la déduction de modèle et c'est par conception.

[classe.accès]/p4

Le contrôle d'accès est appliqué de manière uniforme à tous les noms, qu'ils soient référencés à partir de déclarations ou expression. [...] Accessibilité de l'entité visée par le typedef n'est pas considéré. 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 oeil au même code avec template déduction d'argument impliqué (conceptuellement équivalent à la version auto)

template<class T> 
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 <<"b.i="<< deduce(f.getB())->i <<std::endl; // Prints 'b.i=5'
    return 0;
}

Exemple en direct

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

La sémantique de auto est comme une déduction de modèle implicite (le libellé normatif s'y réfère également directement).

Quelqu'un d'autre avait ce doute avant de.


Maintenant pour les réponses:

Case 1 est facile à d'accord avec si vous considérez que l'appelant n'a pas accès au nom de Foo::Bar.

Case 2 expose également le nom à l'appelant, donc si vous utilisez le nom typedef'd, votre code sera compilé avec plaisir.

6
répondu Marco A. 2017-05-23 12:14:10

La question que vous liez explique beaucoup, mais en complément de ce qui est écrit là-bas...

  1. La principale différence est que, dans la deuxième ligne auto b=... vous laisser le compilateur en déduire le type de l'expression. Vous ne pouvez pas spécifier le type, le nom du type est caché. Le type est cependant utilisable (au moins à partir du compilateur)

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

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

Pour essayer de répondre à la question dans l'en-tête, vous pouvez considérer 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 caché, la forme existe toujours et peut être utilisé.

3
répondu Tasos Vogiatzoglou 2017-05-23 12:06:41