¿Por qué no hay un rasgo de tipo std::is struct?


He visto que para comprobar si un tipo T es una clase puedo usar:

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

Devuelve true tanto para las clases como para las estructuras. Sé que en C++ son casi lo mismo, pero me gustaría saber por qué no hay una distinción entre ellos en el rasgo de tipo. ¿Es siempre inútil comprobar esta diferencia, o hay alguna razón más que no entiendo?

Author: Peter Mortensen, 2015-09-22

5 answers

Devuelve true tanto para las clases como para las estructuras. Sé que en C++ son casi lo mismo, pero me gustaría saber por qué no hay una distinción entre ellos en el rasgo de tipo.

Desafortunadamente este es un error común en C++. A veces proviene de un malentendido fundamental, pero otras veces proviene de una ambigüedad en inglés. Puede provenir de diagnósticos inexactos del compilador, libros mal escritos, respuestas incorrectas {

Probablemente lea algo como esto:

"No hay diferencia en C++ entre una estructura y una clase excepto la visibilidad predeterminada de miembros y bases."

Este pasaje se puede interpretar en un sentido que es engañoso, porque las nociones de identidad e igualdad son difíciles de distinguir cuando se usan frases como "sin diferencia".

De hecho, C++ no ha tenido estructuras desde 1985. Sólo tiene clases.

El tipo de tipos con los que declaras la palabra clave class y la palabra clave struct son clases. Periodo. La palabra clave struct, y las reglas de visibilidad que son las predeterminadas al definir una clase usando esa palabra clave, se mantuvieron solo por compatibilidad con C backward pero eso es una cuestión de sintaxis. No hace que los tipos resultantes sean realmente de un tipo diferente.

El rasgo tipo no hace distinción porque literalmente no hay uno que hacer.

 163
Author: Lightness Races in Orbit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-05 16:54:58

Es imposible distinguir una diferencia en semántica para definiciones vacías como{[14]]}

class C {
public:

};

De

struct S {

};

O de manera similar

class C {

};

Y

struct S {
private:

};

Aparte de la palabra clave struct vs class, no hay diferencia de comportamiento detectable. Véase también esta Pregunta y Respuesta.

Nota : Como señaló @KyleStrand, la derivación también requiere especificadores de acceso explícito, por lo que S : private Base {}; y C : Base {}; son equivalentes, lo mismo que S : Base {}; y C : public Base {};, donde S es una estructura, C es una clase, y Base puede ser cualquiera.

 26
Author: TemplateRex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:02:56

Son la misma cosa. La única diferencia (visibilidad de miembro predeterminada) solo existe en tiempo de compilación. Por lo demás, no hay ninguna diferencia entre struct y class.

ETA: Lo que probablemente quieras es std::is_pod, que le dirá si su clase es un "tipo de datos antiguo". Gran parte de la discusión y los comentarios sobre esta cuestión parecen indicar que esto es lo que realmente quieren aquellos que piensan que debería haber una distinción.

 22
Author: Rob K,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-01 15:16:13

Otros han señalado correctamente que en C++ las palabras clave struct y class tienen el mismo significado excepto por la diferencia en la visibilidad de los miembros.

Si llama a los tipos agregados definidos así "structs" o "classes" o "weiruewzewiruz" depende de usted. En interés de la comunicación, generalmente es aconsejable seguir las convenciones establecidas, por lo que desaconsejaría "weiruewzewiruz".

También se recomienda usar diferencias semánticas como guía para word opción. El uso de struct es más común para datos agregados simples que no tienen mucha lógica interna e invariantes; un uso típico sería struct point { float x; float y; };. Estos tipos a menudo se llaman "estructuras" o "estructuras" en la literatura. No sería sorprendente que alguien usando fprintf en C++ se refiriera al primer argumento como un "puntero a una estructura de ARCHIVO". ARCHIVO es un ejemplo de lo que Scott Meyers 'significa en" C++ más eficaz", Artículo 34:

Es seguro asumir que una estructura definición que compila en ambos los lenguajes [C y C++ - p. a. s] se presentan de la misma manera por ambos compiladores.

En cuanto al lenguaje natural, la elección de la palabra "estructura" no es casual: Meyers está hablando de un simple agregado de datos antiguo que tiene semántica idéntica en ambos idiomas, hasta el nivel de bits.

En cuanto al lenguaje de programación, no importaría si la definición C++ del agregado de datos en cuestión usara la palabra clave struct o class (con un especificador de acceso público). struct es quizás la opción más natural, porque la semántica de C++ del agregado es la semántica de una estructura de C. Además, el uso de struct permite que las fuentes de C y C++ compartan más fácilmente una definición de tipo.

El estándar C++ utiliza "struct" y "structure" tanto en lenguaje natural como de programación, no solo en casos de interoperabilidad: 1.7/5: "Una estructura declarada como", o 3.2/4 struct X; // declare X as a struct type. Lo más interesante es 9/8, sentando las bases para interop-criterios:

8 Una estructura de diseño estándar es una clase de diseño estándar definido con la estructura de clave de clase o la clave de clase clase. [...]

Cómo cualquiera que lea esto puede afirmar que no hay estructuras en C++ está más allá de mí. Esto claramente no es un error de edición porque los términos "struct" y "class" se establecen explícitamente en relación entre sí.


Más interesantes que las elecciones de palabras y las cuestiones de gusto, sin embargo, se manifiestan, diferencias comprobables. ¿Bajo qué circunstancias es un agregado de C++ comparable y compatible con un C struct? Tal vez esta pregunta fue subyacente a su pregunta? El diseño estándar mencionado en la cita es el criterio. Se detalla en 9/7 y esencialmente prescribe que

  • solo una clase en una jerarquía de herencia puede tener definiciones de miembros de datos no estáticos (probablemente porque el estándar no quiere especificar el orden de los elementos de datos definidos en diferentes niveles en tal jerarquía);
  • no se permiten funciones virtuales ni clases base virtuales (debido a los datos de instancia adicionales necesarios para la información de tiempo de ejecución);
  • todos los miembros tienen el mismo "control de acceso" (ya sea público, protegido o privado; probablemente porque una implementación es libre de ordenar por control de acceso).

El estándar entonces dice{[16]]}

9 [Nota: Las clases de diseño estándar son útiles para la comunicación con código escrito en otro lenguajes de programación. Su diseño se especifica en 9.2.-nota final ]

Por supuesto, una definición de estructura que compila en C cumple con estos criterios, de ahí la afirmación de Scott Meyers. FILE de stdio.h es un ejemplo prominente, no del todo trivial. Tenga en cuenta que el estándar no hace garantías porque el diseño de objetos depende de la implementación y puede cambiar solo con una opción de compilador.

Si una clase tiene un diseño estándar se puede probar con el tipo trait std::is_standard_layout<T>. El el siguiente programa, que está inspirado en un ejemplo de cppreference, comprueba los casos principales establecidos en el estándar.

#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>();
}

Sesión de ejemplo:

$ 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
Author: Peter A. Schneider,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-10 11:49:00
C++11 §9/8 ([clase]/8):

" A estructura de diseño estándar es una clase de diseño estándar definida con la clave de clase struct o la clave de clase class. A standard-layout union es una clase de diseño estándar definida con la clave de clase union.

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

" A POD struct es una clase sin unión que es tanto una clase trivial como una clase de diseño estándar, y no tiene miembros de datos no estáticos de tipo non-POD struct, non-POD union (o array de tales tipos). […]

Dado que una estructura POD es una clase de diseño estándar, es un subconjunto de estructura de diseño estándar. Por lo que sé, este es el significado más general de struct en el estándar de C++. Y entonces presumiblemente lo que estás buscando es un rasgo de tipo o conjunto de tipo rasgos que le permite identificar una estructura de diseño estándar como tal.

Y voilà, mirando la lista de rasgos de tipo hay is_class y is_standard_layout. Cuando un tipo satisifies 1both es una "estructura". O más precisamente, es una estructura de diseño estándar , como se define en C++11 §9/8.


Con respecto a

" Me gustaría saber por qué no hay una distinción entre[clase y estructura] en el tipo trait

Bueno, la hay. Esa es la is_standard_layout rasgo.


Con respecto a

" ¿Es siempre inútil comprobar esta diferencia, o hay alguna razón más que no entiendo?

No, no es inútil comprobar esta diferencia. El estándar define standard-layout por la razón de que es muy práctico. Como señala el propio estándar,

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

" [Nota: Las clases de diseño estándar son útiles para comunicarse con código escrito en otros lenguajes de programación. Su diseño se especifica en 9.2.-nota final ]


Notas:
1 El rasgo is_class es verdadero para un class o struct, pero no para un union, a pesar de que el estándar define que "una unión es una clase". Es decir, el rasgo es más específico que la terminología general.

 7
Author: Cheers and hth. - Alf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-22 02:31:30