web-dev-qa-db-fra.com

Pourquoi n'y a-t-il pas un trait de type std :: is_struct?

J'ai vu cela afin de vérifier si un type T est une classe que je peux utiliser:

bool isClass = std::is_class<T>::value;

Il retourne vrai pour les classes et les structures. Je sais qu'en C++, c'est presque la même chose, mais j'aimerais savoir pourquoi il n'y a pas de distinction entre eux dans le trait de type. Est-il toujours inutile de vérifier cette différence, ou y a-t-il une autre raison pour laquelle je ne comprends pas?

55
Jepessen

Il retourne vrai pour les classes et les structures. Je sais qu'en C++, c'est presque la même chose, mais j'aimerais savoir pourquoi il n'y a pas de distinction entre eux dans le trait de type.

Malheureusement, c'est une idée fausse courante en C++. Parfois, cela vient d'un malentendu fondamental, mais à d'autres moments, cela vient d'une ambiguïté en anglais. Cela peut provenir de diagnostics inexacts du compilateur, de livres mal écrits, de réponses incorrectes SO…

Vous avez probablement lu quelque chose comme ça:

"Il n'y a pas de différence en C++ entre une structure et une classe sauf la visibilité par défaut des membres et des bases."

Ce passage peut être interprété dans un sens trompeur, car les notions de identité et égalité sont difficiles à distinguer lorsque l'on utilise des expressions comme "pas de différence".

En fait, C++ n'a pas eu de structures depuis 1985. Il n'a que des classes.

Les types de types que vous déclarez avec le mot clé class et le mot clé struct sont classes . Période. Le mot clé struct, et les règles de visibilité qui sont les valeurs par défaut lors de la définition d'une classe à l'aide de ce mot clé, ont été conservés uniquement pour une compatibilité descendante avec C… mais c'est une chose de syntaxe. Cela ne fait pas que les types résultants soient en fait d'un type différent.

Le trait de type ne fait aucune distinction car il n'y en a littéralement pas un à faire.

167

Il est impossible de distinguer une différence de sémantique pour des définitions vides comme

class C {
public:

};

de

struct S {

};

ou similaire

class C {

};

et

struct S {
private:

};

Mis à part le mot clé struct vs class, aucune différence de comportement n'est détectable. Voir aussi ce Q&A .

Remarque : Comme l'a remarqué @KyleStrand, la dérivation nécessite également des spécificateurs d'accès explicites, donc S : private Base {}; et C : Base {}; sont équivalents, comme S : Base {}; et C : public Base {};, où S est une structure, C est une classe et Base peut être l'une ou l'autre.

27
TemplateRex

C'est la même chose. La seule différence (visibilité des membres par défaut) n'existe qu'au moment de la compilation. Sinon, il n'y a aucune différence entre struct et class.

ETA: Ce que vous voulez probablement, c'est std::is_pod , qui vous dira si votre classe est un "type de données ancien simple". Une grande partie de la discussion et des commentaires sur cette question semble indiquer que c'est ce que veulent réellement ceux qui pensent qu'il devrait y avoir une distinction.

23
Rob K

D'autres ont souligné correctement qu'en C++, les mots clés struct et class ont la même signification à l'exception de la différence de visibilité des membres.

Que vous appeliez des types d'agrégats ainsi définis "structs" ou "classes" ou "weiruewzewiruz" dépend de vous. Dans l'intérêt de la communication, il est généralement conseillé de suivre les conventions établies, donc je déconseille "weiruewzewiruz".

Il est également recommandé d'utiliser les différences sémantiques comme guide pour les choix Word. L'utilisation de struct est plus courante pour les données agrégées simples qui n'ont pas beaucoup de logique interne et d'invariants; une utilisation typique serait struct point { float x; float y; };. Ces types sont souvent appelés "structures" ou "structures" dans la littérature. Il ne serait pas surprenant que quelqu'un utilisant fprintf en C++ fasse référence au premier argument comme un "pointeur vers une structure FILE". FILE est un exemple de ce que Scott Meyers signifie dans "C++ plus efficace", article 34:

Il est sûr de supposer qu'une définition de structure qui compile dans les deux langages [C et C++ -p.a.s] est présentée de la même manière par les deux compilateurs.

En ce qui concerne le langage naturel, la "structure" de choix des mots n'est pas une coïncidence: Meyers parle d'un ancien agrégat de données simple qui a une sémantique identique dans les deux langues, jusqu'au niveau du bit.

En ce qui concerne le langage de programmation, cela n'aurait pas d'importance si la définition C++ de l'agrégat de données en question utilisait le mot clé struct ou class (avec un spécificateur d'accès public). struct est peut-être le choix le plus naturel, car la sémantique C++ de l'agrégat est la sémantique d'une structure C. En outre, l'utilisation de struct permet aux sources C et C++ de partager plus facilement une définition de type.

Le standard C++ utilise "struct" et "structure" aussi bien en langage naturel qu'en langage de programmation, non seulement en cas d'interopérabilité: 1.7/5: "Une structure déclarée comme", ou 3.2/4 struct X; // declare X as a struct type. Le plus intéressant est le 9/8, ouvrant la voie à des critères d'interopérabilité:

8 Une structure de mise en page standard est une classe de mise en page standard définie avec la structure de clé de classe ou la classe de clé de classe. [...]

Comment quiconque lisant ceci peut affirmer qu'il n'y a pas de structures en C++ me dépasse. Ce n'est clairement pas une erreur d'édition car les termes "struct" et "class" sont explicitement définis l'un par rapport à l'autre.


Cependant, les différences manifestes et vérifiables sont plus intéressantes que les choix Word et les questions de goût. Dans quelles circonstances un agrégat C++ est-il comparable et compatible avec un C struct? Peut-être que cette question était à la base de votre question? La disposition standard mentionnée dans le devis est le critère. Il est détaillé dans 9/7 et prescrit essentiellement que

  • une seule classe dans une hiérarchie d'héritage peut avoir des définitions de membres de données non statiques (probablement parce que la norme ne veut pas spécifier l'ordre des éléments de données définis à différents niveaux dans une telle hiérarchie);
  • aucune fonction virtuelle ou classe de base virtuelle n'est autorisée (en raison des données d'instance supplémentaires nécessaires pour les informations d'exécution);
  • tous les membres ont le même "contrôle d'accès" (public, protégé ou privé; probablement parce qu'une mise en œuvre est gratuite à commander par contrôle d'accès).

La norme dit alors

9 [Remarque: les classes de mise en page standard sont utiles pour communiquer avec du code écrit dans d'autres langages de programmation. Leur disposition est spécifiée en 9.2. — note de fin]

Bien sûr, une définition de structure qui compile en C remplit ces critères, d'où l'assertion de Scott Meyers. FILE de stdio.h est un exemple important, pas tout à fait trivial. Notez que la norme ne fait aucune garantie car la disposition des objets dépend de l'implémentation et peut changer uniquement avec une option de compilation.

Si une classe a une disposition standard peut être testée avec le trait de type std::is_standard_layout<T>. Le programme suivant, inspiré de n exemple sur cppreference , vérifie les principaux cas présentés dans la norme.

#include <cstdio>
#include <typeinfo>
#include <type_traits>

using namespace std;

struct funcOnlyT // fine
{
    int f();
};

class podT {   // "class" is ok
    int m1;
    int m2;
};

struct badAccessCtrlT { // bad: public/private
    int m1;
private:
    int m2;
};

struct polymorphicT {  // bad: polymorphic
    int m1;
    int m2;
    virtual void foo();
};


struct inheritOkT: podT // ok: inheritance, data only on one level
{
    int f();
};


struct inheritPlusDataT: podT // bad: inheritance, data on 2 levels
{
    int m3;
};

template<typename T1, typename T2>
struct templT     // ok with std layout types T1, T2
{
    T1 m1;
    T2 m2;
};

// print type "name" and whether it's std layout
template<typename T>
void printIsStdLayout()
{
    printf("%-20s: %s\n", 
            typeid(T).name(),
            std::is_standard_layout<T>::value 
                ? "is std layout" 
                : "is NOT std layout");
}

int main()
{
    printIsStdLayout<funcOnlyT>();
    printIsStdLayout<podT>();
    printIsStdLayout<badAccessCtrlT>();
    printIsStdLayout<polymorphicT>();
    printIsStdLayout<inheritOkT>();
    printIsStdLayout<inheritPlusDataT>();
    printIsStdLayout<templT<int, float> >();
    printIsStdLayout<FILE>();
}

Exemple de session:

$ g++ -std=c++11 -Wall -o isstdlayout isstdlayout.cpp && ./isstdlayout
9funcOnlyT          : is std layout
4podT               : is std layout
14badAccessCtrlT    : is NOT std layout
12polymorphicT      : is NOT std layout
10inheritOkT        : is std layout
16inheritPlusDataT  : is NOT std layout
6templTIifE         : is std layout
9__sFILE64          : is std layout
14
Peter A. Schneider
C++ 11 §9/8 ([classe]/8):

A structure de mise en page standard est une classe de mise en page standard définie avec clé de classestruct ou la clé de classeclass. Une union de mise en page standard est une classe de mise en page standard définie avec la clé de classeunion.

C++ 11 §9/10 ([classe]/10):

A POD struct est une classe non-union qui est à la fois une classe triviale et une classe de mise en page standard, et n'a pas de non-statique membres de données de type non-POD struct, non-POD union (ou tableau de ces types). […]

Etant donné qu'un POD struct est une classe à disposition standard, il s'agit d'un sous-ensemble de standard-layout struct. Pour autant que je sache, c'est la signification la plus générale de struct dans la norme C++. Et donc probablement ce que vous recherchez est un trait de type ou un ensemble de traits de type qui vous permet d'identifier un structure de mise en page standard en tant que tel.

Et voilà, en regardant la liste des traits de caractères, il y a is_class et is_standard_layout. Lorsqu'un type satisfait ¹les deux, c'est une "structure". Ou plus précisément, c'est un structure de mise en page standard, tel que défini par C++ 11 §9/8.


En ce qui concerne

J'aimerais savoir pourquoi il n'y a pas de distinction entre [classe et struct] dans le trait de type

Eh bien, il y en a. C'est le is_standard_layout trait.


En ce qui concerne

Est-il toujours inutile de vérifier cette différence, ou y a-t-il une autre raison que je ne comprends pas?

Non, il n'est pas inutile de vérifier cette différence. La norme définit standard-layout pour la raison que c'est très pratique. Comme le note la norme elle-même,

C++ 11 §9/9 ([classe]/9):

[Remarque: Les classes à présentation standard sont utiles pour communiquer avec du code écrit dans d'autres langages de programmation. Leur disposition est spécifiée en 9.2. — note de fin]


Remarques:
¹ Le is_class est vrai pour un class ou struct, mais pas pour un union, même si la norme définit que "une union est une classe". C'est à dire. le trait est plus spécifique que la terminologie générale.

7