significado punteros perro dogs array c++ pointers

c++ - punteros - ¿Está prohibido el miembro de "estructura interna" de puntero a?



punteros c++ (3)

Como se indica en la respuesta de AnT, esto parece ser una omisión de la norma y la falta de una sintaxis correcta para expresar lo que desea hacer. Un colega mío se enfrentó a este problema el otro día y citó su pregunta y su respuesta como prueba de que no podía hacerse. Bueno, me gusta un desafío, y sí, se puede hacer ... pero no es bonito.

Primero, es importante darse cuenta de que un puntero a miembro es básicamente un desplazamiento de un puntero a estructura [1] . El lenguaje tiene un operador de desplazamiento que es sospechosamente similar a este y, curiosamente, nos da la expresividad necesaria para hacer lo que queremos.

El problema que detectamos de inmediato es que C ++ prohíbe enviar punteros a miembros. Bueno, casi ... tenemos el sindicato echado la manga por esto. Como dije, esto no es bonito!

Por último, también necesitamos saber el tipo de puntero correcto para convertir.

Así que sin más preámbulos, aquí está el código (probado en gcc y clang):

template <typename C, typename T, /*auto*/size_t P> union MemberPointerImpl final { template <typename U> struct Helper { using Type = U C::*; }; template <typename U> struct Helper<U&> { using Type = U C::*; }; using MemberPointer = typename Helper<T>::Type; MemberPointer o; size_t i = P; // we can''t do "auto i" - argh! static_assert(sizeof(i) == sizeof(o)); }; #define MEMBER_POINTER(C, M) / ((MemberPointerImpl<__typeof__(C), / decltype(((__typeof__(C)*)nullptr)->M), / __builtin_offsetof(__typeof__(C), M) / >{ }).o)

Veamos primero la macro MEMBER_POINTER . Se necesitan dos argumentos. La primera, C , es la estructura que será la base para el puntero del miembro. Envolverlo en __typeof__ no es estrictamente necesario, pero permite pasar un tipo o una variable. El segundo argumento, M , proporciona una expresión al miembro del cual queremos el puntero.

La macro MEMBER_POINTER extrae dos datos adicionales de estos argumentos y los pasa como parámetros a la unión de la plantilla MemberPointerImpl . La primera pieza es el tipo de miembro al que se apunta. Esto se hace construyendo una expresión usando un puntero nulo sobre el cual usamos decltype . La segunda pieza es el desplazamiento desde la estructura base al miembro en cuestión.

Dentro de MemberPointerImpl necesitamos construir el tipo MemberPointer que será lo que devuelve la macro. Esto se hace con una estructura auxiliar que elimina las referencias que ocurren de manera poco útil si nuestro miembro es un elemento de matriz, lo que también permite el soporte para esto. También permite que gcc y clang nos den un buen tipo de diagnóstico totalmente expandido si asignamos el valor devuelto a una variable con un tipo no coincidente.

Por lo tanto, para usar MEMBER_POINTER , simplemente cambie su código de:

bool MyStruct::* toto = &MyStruct::inner.c;

a:

bool MyStruct::* toto = MEMBER_POINTER(MyStruct, inner.c);

[1] Ok, advertencia: esto puede no ser cierto para todas las arquitecturas / compiladores, ¡por lo que los escritores de códigos portátiles miran hacia otro lado ahora!

Tengo una estructura anidada y me gustaría tener un puntero a miembro a uno de los miembros anidados:

es legal?

struct InnerStruct { bool c; }; struct MyStruct { bool t; bool b; InnerStruct inner; };

esta:

MyStruct mystruct; //... bool MyStruct::* toto = &MyStruct::b;

esta bien pero

bool MyStruct::* toto = &MyStruct::inner.c;

no es. ¿alguna idea?

Gracias

Aquí hay algunos detalles Sí, es & MyStruct :: by no mystruct :: b; El código es de un sistema RTTI / propiedad personalizado. Para cada clase especificada mantenemos una matriz de "Propiedad", que incluye un Ptr-to-member Se usa de esta manera:

//somewhere else in code... ( myBaseClassWithCustomRTTIPointer)->* toto = true;


El InnerStruct que le importa pasa a estar contenido en una instancia de MyStruct, pero eso no afecta la forma en que obtiene un puntero al miembro del InnerStruct.

bool InnerStruct::* toto2 = &InnerStruct::c;

Edición: al releer su pregunta, supongo que desea definir un puntero a miembro de la estructura externa y que apunte directamente a un miembro de la estructura interna. Eso simplemente no está permitido. Para llegar al miembro de la estructura interna que está contenida en la estructura externa, tendría que crear un puntero a la estructura interna que se encuentra en su lugar, luego a su miembro. Para usarlo, deberías desreferenciar ambos puntos a los miembros:

// Pointer to inner member of MyStruct: InnerStruct MyStruct::* toto = &MyStruct::inner; // Pointer to c member of InnerStruct: bool InnerStruct::* toto2 = &InnerStruct::c; // Dereference both to get to the actual bool: bool x = mystruct.*toto.*toto2;


Sí, está prohibido. No eres el primero en llegar a esta idea perfectamente lógica. En mi opinión, este es uno de los "errores" / "omisiones" obvios en la especificación de punteros a miembros en C ++, pero aparentemente el comité no tiene interés en desarrollar más la especificación de punteros a miembros (como es el caso con la mayoría de las características de lenguaje de "bajo nivel").

Tenga en cuenta que todo lo necesario para implementar la característica ya está allí, en el idioma. Un puntero a un miembro de datos de un miembro no es de ninguna manera diferente de un puntero a un miembro de datos inmediato. Lo único que falta es la sintaxis para inicializar dicho puntero. Sin embargo, el comité aparentemente no está interesado en introducir tal sintaxis.

Desde el punto de vista de la lógica formal pura, esto debería haberse permitido en C ++

struct Inner { int i; int j[10]; }; struct Outer { int i; int j[10]; Inner inner; }; Outer o; int Outer::*p; p = &Outer::i; // OK o.*p = 0; // sets `o.i` to 0 p = &Outer::inner.i; // ERROR, but should have been supported o.*p = 0; // sets `o.inner.i` to 0 p = &Outer::j[0]; // ERROR, but should have been supported o.*p = 0; // sets `o.j[0]` to 0 // This could have been used to implement something akin to "array type decay" // for member pointers p = &Outer::j[3]; // ERROR, but should have been supported o.*p = 0; // sets `o.j[3]` to 0 p = &Outer::inner.j[5]; // ERROR, but should have been supported o.*p = 0; // sets `o.inner.j[5]` to 0

Una implementación típica de miembro de puntero a datos no es más que un desplazamiento de bytes del miembro desde el principio del objeto que lo contiene. Dado que todos los miembros (inmediatos y miembros de miembros) se presentan en última instancia de forma secuencial en la memoria, los miembros de los miembros también pueden identificarse por un valor de compensación específico. Esto es lo que quiero decir cuando digo que el funcionamiento interno de esta función ya está completamente implementado, todo lo que se necesita es la sintaxis de inicialización.

En lenguaje C, esta funcionalidad se emula mediante las compensaciones explícitas obtenidas a través de la macro offsetof estándar. Y en CI puede obtener offsetof(Outer, inner.i) y offsetof(Outer, j[2]) . Desafortunadamente, esta capacidad no se refleja en los miembros de punteros a datos de C ++.