C ++ carece del equivalente de la self
palabra clave de PHP , que se evalúa según el tipo de la clase adjunta.
Es bastante fácil fingirlo por clase:
struct Foo
{
typedef Foo self;
};
pero tuve que escribir de Foo
nuevo. Tal vez algún día me equivoque y cause un error silencioso.
¿Puedo usar alguna combinación de decltype
y amigos para hacer que esto funcione de manera "autónoma"? Ya intenté lo siguiente, pero this
no es válido en ese lugar:
struct Foo
{
typedef decltype(*this) self;
};
// main.cpp:3:22: error: invalid use of 'this' at top level
// typedef decltype(*this) self;
(No me voy a preocupar por el equivalente de static
, que hace lo mismo pero con enlace tardío).
this_t
probablemente estaría más alineado con los nombres normales de C ++.auto()
y~auto()
para ctors / dtors. Interesante por decir lo menos. Si se usa para ese propósito, tal veztypedef auto self;
, pero eso me parece un poco esquemático.decltype(class)
, tal vez con undecltype(struct)
equivalente. Eso es mucho más claro que soloauto
en un contexto específico y no veo ningún problema con que encaje en el lenguaje basado endecltype(auto)
.void _check() { static_assert(std::is_same<self&, decltype(*this)>::value, "Correct your self type"); }
No funciona con plantillas de clase, aunque ...Respuestas:
Así es como puede hacerlo sin repetir el tipo de Foo:
Si desea derivar de
Foo
, debe usar la macroWITH_SELF_DERIVED
de la siguiente manera:Incluso puede hacer herencia múltiple con tantas clases base como desee (gracias a plantillas variadas y macros variadic):
He verificado que esto funciona en gcc 4.8 y clang 3.4.
fuente
auto
ydecltype
o en este caso deself
.template<typename T>class Self{protected: typedef T self;}; class WITH_SELF(Foo) : public Bar, private Baz {};
habría sido más simple y permitiría un control más preciso sobre la herencia, ¿hay alguna razón en contra?Una posible solución alternativa (ya que todavía tiene que escribir el tipo una vez):
Para una versión más segura podríamos asegurar que en
T
realidad se deriva deSelf<T>
:Tenga en cuenta que una
static_assert
función miembro dentro de una es probablemente la única forma de verificar, ya que los tipos pasadosstd::is_base_of
deben estar completos.fuente
typename
en el typedef. Y dado que esto no reduce el número de despidos, no creo que sea una alternativa viable.Foo
nombre.Foo
, debe: (1) propagar la T hacia arriba al descendiente de la hoja, o (2) recordar heredar de SelfT muchas veces , o (3) aceptar que todos los niños creen que es la Base ... utilizable, pero poco agradable.self
lugar destatic
, no hay problema.Puede usar una macro en lugar de una declaración de clase normal, que lo hará por usted.
Y luego usa like
#define END_CLASS };
probablemente ayudaría a la legibilidad.También puede tomar @ Paranaix
Self
y usarlo (comienza a ponerse realmente pirateado)fuente
{
, por lo que}
está "colgado", lo que probablemente a los editores de texto tampoco les gustará.CLASS_WITH_SELF(foo) { … };
- y creo que eso es imposible de lograr.Self
.No tengo pruebas positivas, pero creo que es imposible. Lo siguiente falla, por la misma razón que su intento, y creo que eso es lo más lejos que podemos llegar:
Esencialmente, lo que esto demuestra es que el alcance en el que queremos declarar nuestra typedef simplemente no tiene acceso (ya sea directo o indirecto)
this
y no hay otra forma (independiente del compilador) de llegar al tipo o nombre de la clase.fuente
decltype
es un contexto no evaluado, por lo que invocar la función del miembro no es el problema (eso no se intentará)struct S { int i; typedef decltype(i) Int; };
funciona aunquei
sea un miembro de datos no estático. Funciona porquedecltype
tiene una excepción especial en la que un nombre simple no se evalúa como expresión. Pero no puedo pensar en ninguna forma de utilizar esta posibilidad de una manera que responda a la pregunta.Lo que funciona tanto en GCC como en clang es crear una typedef que haga referencia al
this
usothis
en el tipo de retorno de una función typedef. Dado que esta no es la declaración de una función miembro estática,this
se tolera el uso de . Luego puede usar ese typedef para definirself
.Desafortunadamente, una lectura estricta del estándar dice que incluso esto no es válido. Lo que hace clang es comprobar que
this
no se utiliza en la definición de una función miembro estática. Y aquí, de hecho, no lo es. A GCC no le importa sithis
se usa en un tipo de retorno al final independientemente del tipo de función, lo permite incluso parastatic
funciones miembro. Sin embargo, lo que el estándar realmente requiere es quethis
no se use fuera de la definición de función miembro no estática (o inicializador de miembro de datos no estático). Intel lo hace bien y lo rechaza.Dado que:
this
solo está permitido en inicializadores de miembros de datos no estáticos y funciones de miembros no estáticos ([expr.prim.general] p5),this
se pueden usar ([over.call.func] p3),Creo que puedo decir de manera concluyente que no hay forma de implementarlo
self
sin incluir de alguna manera, en algún lugar, el nombre del tipo.Editar : Hay una falla en mi razonamiento anterior. "Las funciones miembro no estáticas solo se pueden llamar por un nombre no calificado, incluso en contextos no evaluados, cuando esto se puede usar ([over.call.func] p3)", es incorrecto. Lo que realmente dice es
Dentro de una función miembro estática,
this
puede que no aparezca, pero aún existe.Sin embargo, según los comentarios, dentro de una función miembro estática, la transformación de
f()
a(*this).f()
no se realizaría, y si no se realiza, se infringe [expr.call] p1:ya que no habría acceso de miembros. Así que incluso eso no funcionaría.
fuente
_self_fn_1()
se "transforma" en(*this)._self_fn_1()
. Sin embargo, no estoy seguro de si eso lo hace ilegal.X
en un contexto dondethis
se puede usar", por lo que no creo que se realice una transformación.auto _self_fn_1() -> decltype(*this); auto _self_fn_1() const -> decltype(*this);
?)const
embargo, sobre las sobrecargas: eso no es un problema. 5.1p3 se ha modificado específicamente para aplicarse también a funciones miembro estáticas, y dice que el tipo dethis
esFoo*
/Bar*
(sinconst
), porque no hayconst
en la declaración de_self_fn_2
.esto no funciona en tipos de plantilla, ya
self_check
que no se llama, por lostatic_assert
que no se evalúa.Podemos hacer algunos trucos para que funcione también para
template
s, pero tiene un costo menor de tiempo de ejecución.struct
se crea un vacío de 1 byte de tamaño en su clase. Si se crea una instancia de su tipo,self
se prueba con.fuente
template
opciones de soporte de clase.inline
. Eso significa que no necesitas escribirinline
nada. Entonces, si ha estado escribiendoinline
frente a cada definición de función de miembro de clase durante toda su carrera, puede detenerse ahora;)También creo que es imposible, aquí hay otro intento fallido, pero en mi humilde opinión, interesante que evita el
this
acceso:que falla porque C ++ requiere que califiques
self_f
con la clase cuando quieras tomar su dirección :(fuente
int T::*
puntero regular a una variable miembro. Yint self_var; typedef decltype(&self_var) self_ptr
tampoco funciona, es algo normalint*
.Recientemente descubrí que
*this
está permitido en un inicializador de llave o igual . Descrito en § 5.1.1 ( del borrador de trabajo n3337 ):Con eso en mente, el siguiente código:
pasa el de Daniel Frey
static_assert
.Live example
fuente
test
embargo= this
, ¿verdad? Y por qué no solousing self = Foo*;
test
a ser de tipo, um,Foo *
!A menos que el tipo deba ser un tipo de miembro de la clase adjunta, puede reemplazar el uso de
self
condecltype(*this)
. Si lo usa en muchos lugares de su código, puede definir una macro de laSELF
siguiente manera:fuente
self
referirse a la clase inmediatamente circundante y no a la clase exterior? Pero no sé php tan bien.Proporcione mi versión. Lo mejor es que su uso es el mismo que el de la clase nativa. Sin embargo, no funciona para clases de plantilla.
fuente
Sobre la base de la respuesta de hvd, descubrí que lo único que faltaba era eliminar la referencia, por eso falla la verificación std :: is_same (b / c, el tipo resultante es en realidad una referencia al tipo). Ahora, esta macro sin parámetros puede hacer todo el trabajo. Ejemplo de trabajo a continuación (uso GCC 8.1.1).
fuente
Repetiré la solución obvia de "tener que hacerlo tú mismo". Esta es la versión sucinta C ++ 11 del código, que funciona tanto con clases simples como con plantillas de clases:
Puedes verlo en acción en ideone . La génesis que conduce a este resultado es la siguiente:
Esto tiene el problema obvio de copiar y pegar el código en una clase diferente y olvidar cambiar XYZ, como aquí:
Mi primer enfoque no fue muy original: hacer una función como esta:
Es un poco largo, pero por favor tengan paciencia conmigo. Esto tiene la ventaja de trabajar en C ++ 03 sin
decltype
, ya que la__self_check_helper
función se emplea para deducir el tipo dethis
. Además, no haystatic_assert
, pero en susizeof()
lugar se emplea el truco. Podría hacerlo mucho más corto para C ++ 0x. Ahora bien, esto no funcionará para las plantillas. Además, hay un problema menor con la macro que no espera el punto y coma al final, si se compila con pedante, se quejará de un punto y coma innecesario adicional (o se quedará con una macro de aspecto extraño que no termina en punto y coma en el cuerpo deXYZ
yABC
).Hacer una verificación en el
Type
que se pasaDECLARE_SELF
no es una opción, ya que eso solo verificaría laXYZ
clase (que está bien), ajena aABC
(que tiene un error). Y entonces me di cuenta. Una solución de almacenamiento sin costo adicional que funciona con plantillas:Esto simplemente hace una aserción estática en un valor de enumeración único (o al menos único en caso de que no escriba todo su código en una sola línea), no se emplea ningún truco de comparación de tipos y funciona como aserción estática, incluso en plantillas . Y como beneficio adicional, ahora se requiere el punto y coma final :).
Me gustaría agradecer a Yakk por darme una buena inspiración. No escribiría esto sin antes ver su respuesta.
Probado con VS 2008 y g ++ 4.6.3. De hecho, con el ejemplo
XYZ
yABC
, se queja:Ahora, si hacemos de ABC una plantilla:
Obtendremos:
Solo se activó la verificación del número de línea, ya que la verificación de la función no se compiló (como se esperaba).
Con C ++ 0x (y sin los guiones bajos malvados), solo necesitaría:
Creo que, lamentablemente, el bit CStaticAssert todavía es necesario ya que produce un tipo, que se define en el cuerpo de la plantilla (supongo que no se puede hacer lo mismo
static_assert
). La ventaja de este enfoque sigue siendo su coste cero.fuente
static_assert
aquí, ¿no? Además, su código completo no es válido porque está utilizando identificadores ilegales (reservados).No sé todo sobre estas plantillas extravagantes, ¿qué tal algo súper simple?
Trabajo hecho, a menos que no puedas soportar un par de macros. Incluso puede usar
CLASSNAME
para declarar su (s) constructor (es) (y, por supuesto, destructor).Demo en vivo .
fuente