¿Diferencia entre 'estructura' y 'estructura typedef' en C++?

Resuelto criddell asked hace 15 años • 8 respuestas

En C++ , ¿hay alguna diferencia entre:

struct Foo { ... };

y:

typedef struct { ... } Foo;
criddell avatar Mar 05 '09 03:03 criddell
Aceptado

En C++, sólo hay una diferencia sutil. Es un vestigio de C, en el que marca la diferencia.

El estándar del lenguaje C ( C89 §3.1.2.3 , C99 §6.2.3 y C11 §6.2.3 ) exige espacios de nombres separados para diferentes categorías de identificadores, incluidos identificadores de etiquetas (para struct/ union/ enum) e identificadores ordinarios (para typedefy otros identificadores) .

Si acabas de decir:

struct Foo { ... };
Foo x;

obtendría un error del compilador, porque Foosolo está definido en el espacio de nombres de la etiqueta.

Tendrías que declararlo como:

struct Foo x;

Cada vez que quieras hacer referencia a Foo, siempre tendrás que llamarlo struct Foo. Esto se vuelve molesto rápidamente, por lo que puedes agregar un typedef:

struct Foo { ... };
typedef struct Foo Foo;

Ahora struct Foo(en el espacio de nombres de la etiqueta) y simplemente Foo(en el espacio de nombres del identificador ordinario) ambos se refieren a lo mismo, y puedes declarar libremente objetos de tipo Foosin la structpalabra clave.


El constructo:

typedef struct Foo { ... } Foo;

es solo una abreviatura de la declaración y typedef.


Finalmente,

typedef struct { ... } Foo;

declara una estructura anónima y crea una typedefpara ella. Por lo tanto, con esta construcción, no tiene un nombre en el espacio de nombres de la etiqueta, solo un nombre en el espacio de nombres typedef. Esto significa que tampoco se puede declarar anticipadamente. Si desea realizar una declaración directa, debe asignarle un nombre en la etiqueta namespace .


En C++, todas las declaraciones struct// union/ actúan como si estuvieran implícitamente , siempre y cuando el nombre no esté oculto por otra declaración con el mismo nombre. Consulte la respuesta de Michael Burr para obtener todos los detalles.enumclasstypedef

Adam Rosenfield avatar Mar 04 '2009 20:03 Adam Rosenfield

En este artículo de DDJ , Dan Saks explica una pequeña área donde los errores pueden aparecer si no escribes tus estructuras (¡y clases!):

Si lo desea, puede imaginar que C++ genera un typedef para cada nombre de etiqueta, como

typedef class string string;

Desafortunadamente, esto no es del todo exacto. Ojalá fuera así de sencillo, pero no lo es. C++ no puede generar tales definiciones de tipos para estructuras, uniones o enumeraciones sin introducir incompatibilidades con C.

Por ejemplo, supongamos que un programa en C declara una función y una estructura denominada estado:

int status(); struct status;

Nuevamente, esto puede ser una mala práctica, pero es C. En este programa, el estado (por sí mismo) se refiere a la función; El estado de la estructura se refiere al tipo.

Si C++ generara automáticamente typedefs para etiquetas, entonces cuando compilaras este programa como C++, el compilador generaría:

typedef struct status status;

Desafortunadamente, el nombre de este tipo entraría en conflicto con el nombre de la función y el programa no se compilaría. Es por eso que C++ no puede simplemente generar un typedef para cada etiqueta.

En C++, las etiquetas actúan igual que los nombres typedef, excepto que un programa puede declarar un objeto, función o enumerador con el mismo nombre y el mismo alcance que una etiqueta. En ese caso, el nombre del objeto, función o enumerador oculta el nombre de la etiqueta. El programa puede hacer referencia al nombre de la etiqueta solo utilizando la palabra clave class, struct, union o enum (según corresponda) delante del nombre de la etiqueta. Un nombre de tipo que consta de una de estas palabras clave seguida de una etiqueta es un especificador de tipo elaborado. Por ejemplo, el estado de la estructura y el mes de enumeración son especificadores de tipo elaborados.

Por tanto, un programa en C que contenga ambos:

int status(); struct status;

Se comporta igual cuando se compila como C++. El nombre y el estado por sí solos se refieren a la función. El programa puede hacer referencia al tipo sólo utilizando el estado de estructura del especificador de tipo elaborado.

Entonces, ¿cómo permite esto que los errores se introduzcan en los programas? Considere el programa del Listado 1 . Este programa define una clase foo con un constructor predeterminado y un operador de conversión que convierte un objeto foo a char const *. La expresion

p = foo();

en main debería construir un objeto foo y aplicar el operador de conversión. La declaración de salida posterior

cout << p << '\n';

debería mostrar la clase foo, pero no es así. Muestra la función foo.

Este resultado sorprendente ocurre porque el programa incluye el encabezado lib.h que se muestra en el Listado 2 . Este encabezado define una función también llamada foo. El nombre de la función foo oculta el nombre de la clase foo, por lo que la referencia a foo en main se refiere a la función, no a la clase. main puede referirse a la clase sólo usando un especificador de tipo elaborado, como en

p = class foo();

La forma de evitar dicha confusión en todo el programa es agregar el siguiente typedef para el nombre de clase foo:

typedef class foo foo;

inmediatamente antes o después de la definición de clase. Este typedef provoca un conflicto entre el nombre del tipo foo y el nombre de la función foo (de la biblioteca) que provocará un error en tiempo de compilación.

No conozco a nadie que realmente escriba estos tipos de definición de forma natural. Requiere mucha disciplina. Dado que la incidencia de errores como el del Listado 1 es probablemente bastante pequeña, es posible que nunca se enfrente a este problema. Pero si un error en su software puede causar lesiones corporales, entonces debe escribir los typedefs sin importar cuán improbable sea el error.

No puedo imaginar por qué alguien querría ocultar un nombre de clase con un nombre de función u objeto en el mismo ámbito que la clase. Las reglas de ocultación en C fueron un error y no deberían haberse extendido a las clases en C++. De hecho, puede corregir el error, pero requiere disciplina y esfuerzo de programación adicionales que no deberían ser necesarios.

Michael Burr avatar Mar 04 '2009 21:03 Michael Burr

Una diferencia más importante: typedeflos s no se pueden declarar hacia adelante. Entonces, para la typedefopción, debe #includeel archivo que contiene typedef, es decir, todo lo que #includees suyo .htambién incluye ese archivo, ya sea que lo necesite directamente o no, y así sucesivamente. Definitivamente puede afectar los tiempos de construcción en proyectos más grandes.

Sin typedef, en algunos casos puede simplemente agregar una declaración directa de struct Foo;en la parte superior de su .harchivo y solo #includela definición de estructura en su .cpparchivo.

Joe avatar Mar 04 '2009 20:03 Joe

Hay una diferencia, pero sutil. Mírelo de esta manera: struct Foointroduce un nuevo tipo. El segundo crea un alias llamado Foo (y no un tipo nuevo) para un structtipo sin nombre.

7.1.3 El especificador typedef

1 [...]

Un nombre declarado con el especificador typedef se convierte en un nombre typedef. Dentro del alcance de su declaración, un nombre-typedef es sintácticamente equivalente a una palabra clave y nombra el tipo asociado con el identificador en la forma descrita en la Cláusula 8. Por tanto, un nombre-typedef es sinónimo de otro tipo. Un typedef-name no introduce un nuevo tipo como lo hace una declaración de clase (9.1) o una declaración de enumeración.

8 Si la declaración de typedef define una clase (o enumeración) sin nombre, el primer nombre de typedef declarado por la declaración como ese tipo de clase (o tipo de enumeración) se usa para indicar el tipo de clase (o tipo de enumeración) solo para fines de vinculación ( 3.5). [ Ejemplo:

typedef struct { } *ps, S; // S is the class name for linkage purposes

Por lo tanto, un typedef siempre se utiliza como marcador de posición/sinónimo de otro tipo.

dirkgently avatar Mar 04 '2009 20:03 dirkgently

No puede utilizar la declaración directa con la estructura typedef.

La estructura en sí es de tipo anónimo, por lo que no tiene un nombre real para reenviar la declaración.

typedef struct{
    int one;
    int two;
}myStruct;

Una declaración directa como esta no funcionará:

struct myStruct; //forward declaration fails

void blah(myStruct* pStruct);

//error C2371: 'myStruct' : redefinition; different basic types
Yochai Timmer avatar Aug 05 '2013 09:08 Yochai Timmer