C ++ não possui o equivalente da palavra-chave do PHPself
, que avalia o tipo da classe envolvente.
É muito fácil fingir em uma base por classe:
struct Foo
{
typedef Foo self;
};
mas eu tive que escrever Foo
novamente. Talvez um dia eu me engane e cause um bug silencioso.
Posso usar alguma combinação de decltype
e amigos para tornar este trabalho "autônomo"? Já tentei o seguinte, mas this
não é válido naquele lugar:
struct Foo
{
typedef decltype(*this) self;
};
// main.cpp:3:22: error: invalid use of 'this' at top level
// typedef decltype(*this) self;
(Não vou me preocupar com o equivalente de static
, que faz o mesmo, mas com ligação tardia.)
this_t
provavelmente estaria mais alinhado com a nomenclatura C ++ regular.auto()
e~auto()
para ctors / dtors. Interessante para dizer o mínimo. Se usado para esse propósito, talveztypedef auto self;
, mas parece um pouco vago para mim.decltype(class)
, talvez com umdecltype(struct)
equivalente. Isso é muito mais claro do que apenasauto
em um contexto específico e não vejo problemas em se adequar à linguagem baseada emdecltype(auto)
.void _check() { static_assert(std::is_same<self&, decltype(*this)>::value, "Correct your self type"); }
Não funciona com modelos de classe, embora ...Respostas:
Veja como você pode fazer isso sem repetir o tipo de Foo:
Se você deseja derivar
Foo
, deve usar a macroWITH_SELF_DERIVED
da seguinte maneira:Você pode até fazer herança múltipla com quantas classes de base quiser (graças a modelos e macros variados):
Eu verifiquei que isso funciona no gcc 4.8 e no clang 3.4.
fonte
auto
edecltype
ou, neste caso deself
.template<typename T>class Self{protected: typedef T self;}; class WITH_SELF(Foo) : public Bar, private Baz {};
teria sido mais simples e permitiria um controle mais preciso sobre a herança - algum motivo contra?Uma possível solução alternativa (já que você ainda precisa escrever o tipo uma vez):
Para uma versão mais segura, podemos garantir que
T
realmente deriva deSelf<T>
:Observe que
static_assert
dentro de uma função de membro é provavelmente a única maneira de verificar, já que os tipos passadosstd::is_base_of
devem ser completos.fonte
typename
no typedef. E como isso não reduz o número de demissões, não acho que seja uma alternativa viável.Foo
nome.Foo
, você tem que: (1) propagar o T para cima para o descendente de folha, ou (2) lembrar de herdar de SelfT muitas vezes , ou (3) aceitar que todas as coisas filhos sejam a Base .. utilizável, mas desagradável.self
vez destatic
, não há problema.Você pode usar uma macro em vez de uma declaração de classe regular, isso fará isso por você.
E então use como
#define END_CLASS };
provavelmente ajudaria na legibilidade.Você também pode pegar o @ Paranaix's
Self
e usá-lo (começa a ficar muito hackeado)fonte
{
, então o}
está "suspenso", o que os editores de texto provavelmente também não gostariam.CLASS_WITH_SELF(foo) { … };
- e acho que isso é impossível de conseguir.Self
.Não tenho evidências positivas, mas acho que é impossível. O seguinte falha - pelo mesmo motivo de sua tentativa - e acho que é o mais longe que podemos chegar:
Essencialmente, o que isso demonstra é que o escopo no qual queremos declarar nosso typedef simplesmente não tem acesso (seja direto ou indireto)
this
e não há outra maneira (independente do compilador) de chegar ao tipo ou nome da classe.fonte
decltype
é um contexto não avaliado, portanto, invocar a função de membro não é o problema (isso não será tentado)struct S { int i; typedef decltype(i) Int; };
funciona mesmo sendoi
um membro de dados não estático. Funciona porquedecltype
tem uma exceção especial em que um nome simples não é avaliado como uma expressão. Mas não consigo pensar em nenhuma maneira de usar essa possibilidade de uma forma que responda à pergunta.O que funciona tanto no GCC quanto no clang é criar um typedef que se refere ao
this
usothis
no tipo de retorno à direita de um typedef de função. Uma vez que esta não é a declaração de uma função de membro estática, o uso dethis
é tolerado. Você pode então usar esse typedef para definirself
.Infelizmente, uma leitura estrita da norma diz que mesmo isso não é válido. O que o clang faz é verificar o que
this
não é usado na definição de uma função de membro estática. E aqui, de fato não é. O GCC não se importa sethis
for usado em um tipo de retorno à direita, independentemente do tipo de função, ele permite até mesmo parastatic
funções de membro. No entanto, o que o padrão realmente requer é quethis
não seja usado fora da definição de uma função de membro não estática (ou inicializador de membro de dados não estático). A Intel acerta e rejeita isso.Dado que:
this
só é permitido em inicializadores de membros de dados não estáticos e funções de membros não estáticos ([expr.prim.general] p5),this
podem ser usadas ([over.call.func] p3),Acho que posso dizer de forma conclusiva que não há maneira de implementar
self
sem incluir de alguma forma, em algum lugar, o nome do tipo.Edit : Há uma falha no meu raciocínio anterior. "funções de membro não estáticas só podem ser chamadas por nome não qualificado, mesmo em contextos não avaliados, quando isso pode ser usado ([over.call.func] p3)," está incorreto. O que realmente diz é
Dentro de uma função de membro estático,
this
pode não aparecer, mas ainda existe.No entanto, de acordo com os comentários, dentro de uma função de membro estática, a transformação de
f()
para(*this).f()
não seria realizada, e se isso não for realizado, então [expr.call] p1 é violado:pois não haveria acesso de membro. Portanto, mesmo isso não funcionaria.
fonte
_self_fn_1()
é "transformado" em(*this)._self_fn_1()
. Não tenho certeza se isso o torna ilegal.X
em um contexto ondethis
pode ser usado", então não acho que a transformação seja realizada.auto _self_fn_1() -> decltype(*this); auto _self_fn_1() const -> decltype(*this);
?)const
sobrecargas, porém: isso não é um problema. 5.1p3 foi modificado especificamente para se aplicar também a funções-membro estáticas, e diz que o tipo dethis
éFoo*
/Bar*
(semconst
), porque não háconst
na declaração de_self_fn_2
.isso não funciona em tipos de modelo, pois
self_check
não é chamado, portanto, ostatic_assert
não é avaliado.Podemos fazer alguns hacks para fazê-lo funcionar para
template
s também, mas tem um custo menor de tempo de execução.um vazio
struct
de tamanho 1 byte é criado em sua classe. Se o seu tipo for instanciado,self
será testado.fonte
template
opções de suporte de classe.inline
,. Isso significa que você não precisa escreverinline
nada. Portanto, se você tem escritoinline
antes de cada definição de função de membro de classe em toda a sua carreira, pode parar agora;)Eu também acho que é impossível, aqui está outra tentativa falha mas interessante IMHO que evita o
this
-access:que falha porque C ++ exige que você se qualifique
self_f
com a classe quando quiser pegar seu endereço :(fonte
int T::*
ponteiro regular para uma variável de membro. Eint self_var; typedef decltype(&self_var) self_ptr
também não funciona, é apenas um normalint*
.Recentemente descobri que isso
*this
é permitido em um inicializador de chave ou igual . Descrito no § 5.1.1 ( do esboço de trabalho n3337 ):Com isso em mente, o seguinte código:
passa de Daniel Frey
static_assert
.Live example
fonte
test
embora= this
, certo? E por que não apenasusing self = Foo*;
test
ser do tipo, humFoo *
,!A menos que o tipo precise ser um tipo de membro da classe envolvente, você pode substituir o uso
self
pordecltype(*this)
. Se você usá-lo em muitos lugares em seu código, poderá definir uma macro daSELF
seguinte maneira:fonte
self
referir à classe imediatamente envolvente e não à classe externa? Mas não conheço muito bem o php.Forneça minha versão. O melhor é que seu uso é igual ao da classe nativa. No entanto, isso não funciona para classes de modelo.
fonte
Com base na resposta de hvd, descobri que a única coisa que faltava era remover a referência, é por isso que a verificação std :: is_same falha (b / c o tipo resultante é na verdade uma referência ao tipo). Agora, esta macro sem parâmetros pode fazer todo o trabalho. Exemplo de trabalho abaixo (eu uso o GCC 8.1.1).
fonte
Vou repetir a solução óbvia de "ter que fazer você mesmo". Esta é a versão C ++ 11 sucinta do código, que funciona com classes simples e modelos de classe:
Você pode ver isso em ação no ideone . A gênese que levou a esse resultado está abaixo:
Isso tem o problema óbvio de copiar e colar o código para uma classe diferente e esquecer de alterar XYZ, como aqui:
Minha primeira abordagem não foi muito original - fazer uma função, como esta:
É meio demorado, mas, por favor, tenha paciência comigo aqui. Isso tem a vantagem de trabalhar em C ++ 03 sem
decltype
, já que a__self_check_helper
função é empregada para deduzir o tipo dethis
. Além disso, não hástatic_assert
, mas osizeof()
truque é empregado. Você poderia torná-lo muito mais curto para C ++ 0x. Agora, isso não funcionará para modelos. Além disso, há um pequeno problema com a macro que não espera ponto-e-vírgula no final; se compilar com pedante, ela reclamará de um ponto-e-vírgula desnecessário extra (ou você ficará com uma macro de aparência estranha que não termina em ponto-e-vírgula no corpo deXYZ
eABC
)Verificar o
Type
que é passadoDECLARE_SELF
não é uma opção, pois isso apenas verificaria aXYZ
classe (que está ok), esquecidoABC
(que tem erro). E então isso me atingiu. Uma solução de custo zero sem armazenamento adicional que funciona com modelos:Isso simplesmente faz asserção estática em um valor enum único (ou pelo menos único no caso de você não escrever todo o seu código em uma única linha), nenhum truque de comparação de tipo é empregado e funciona como assert estático, mesmo em modelos . E como um bônus - o ponto-e-vírgula final agora é necessário :).
Gostaria de agradecer a Yakk por me dar uma boa inspiração. Eu não escreveria isso sem primeiro ver sua resposta.
Testado com VS 2008 e g ++ 4.6.3. Na verdade, com o exemplo
XYZ
eABC
, ele reclama:Agora, se fizermos do ABC um modelo:
Nós conseguiremos:
Apenas a verificação do número da linha foi acionada, pois a verificação da função não foi compilada (como esperado).
Com C ++ 0x (e sem os sublinhados malignos), você precisaria apenas:
Acredito que o bit CStaticAssert infelizmente ainda seja necessário, pois produz um tipo, que é digitado no corpo do modelo (suponho que o mesmo não pode ser feito com
static_assert
). A vantagem dessa abordagem ainda é seu custo zero.fonte
static_assert
aqui, não é? Além disso, seu código completo é inválido porque você está usando identificadores ilegais (reservados).Não sei tudo sobre esses modelos malucos, que tal algo super simples:
Trabalho concluído, a menos que você não aguente algumas macros. Você pode até usar
CLASSNAME
para declarar seu (s) construtor (es) (e, é claro, destruidor).Demonstração ao vivo .
fonte