C ++ carece del equivalente de la selfpalabra 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 Foonuevo. Tal vez algún día me equivoque y cause un error silencioso.
¿Puedo usar alguna combinación de decltypey amigos para hacer que esto funcione de manera "autónoma"? Ya intenté lo siguiente, pero thisno 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_tprobablemente 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 soloautoen 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_DERIVEDde 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
autoydecltypeo 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
Trealidad se deriva deSelf<T>:Tenga en cuenta que una
static_assertfunción miembro dentro de una es probablemente la única forma de verificar, ya que los tipos pasadosstd::is_base_ofdeben estar completos.fuente
typenameen el typedef. Y dado que esto no reduce el número de despidos, no creo que sea una alternativa viable.Foonombre.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.selflugar 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
Selfy 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)
thisy no hay otra forma (independiente del compilador) de llegar al tipo o nombre de la clase.fuente
decltypees 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 aunqueisea un miembro de datos no estático. Funciona porquedecltypetiene 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
thisusothisen el tipo de retorno de una función typedef. Dado que esta no es la declaración de una función miembro estática,thisse 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
thisno 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 sithisse usa en un tipo de retorno al final independientemente del tipo de función, lo permite incluso parastaticfunciones miembro. Sin embargo, lo que el estándar realmente requiere es quethisno 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:
thissolo está permitido en inicializadores de miembros de datos no estáticos y funciones de miembros no estáticos ([expr.prim.general] p5),thisse pueden usar ([over.call.func] p3),Creo que puedo decir de manera concluyente que no hay forma de implementarlo
selfsin 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,
thispuede 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.Xen un contexto dondethisse 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);?)constembargo, 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 dethisesFoo*/Bar*(sinconst), porque no hayconsten la declaración de_self_fn_2.esto no funciona en tipos de plantilla, ya
self_checkque no se llama, por lostatic_assertque no se evalúa.Podemos hacer algunos trucos para que funcione también para
templates, pero tiene un costo menor de tiempo de ejecución.structse crea un vacío de 1 byte de tamaño en su clase. Si se crea una instancia de su tipo,selfse prueba con.fuente
templateopciones de soporte de clase.inline. Eso significa que no necesitas escribirinlinenada. Entonces, si ha estado escribiendoinlinefrente 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
thisacceso:que falla porque C ++ requiere que califiques
self_fcon 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_ptrtampoco funciona, es algo normalint*.Recientemente descubrí que
*thisestá 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
testembargo= this, ¿verdad? Y por qué no solousing self = Foo*;testa 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
selfcondecltype(*this). Si lo usa en muchos lugares de su código, puede definir una macro de laSELFsiguiente manera:fuente
selfreferirse 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_helperfunció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 deXYZyABC).Hacer una verificación en el
Typeque se pasaDECLARE_SELFno es una opción, ya que eso solo verificaría laXYZclase (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
XYZyABC, 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_assertaquí, ¿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
CLASSNAMEpara declarar su (s) constructor (es) (y, por supuesto, destructor).Demo en vivo .
fuente