C ++ nie ma odpowiednika słowa kluczowego PHPself
, które ocenia typ otaczającej klasy.
Łatwo jest sfałszować to na podstawie klasy:
struct Foo
{
typedef Foo self;
};
ale musiałem napisać Foo
ponownie. Może któregoś dnia się pomylę i wywołam cichy błąd.
Czy mogę użyć kombinacji decltype
przyjaciół i przyjaciół, aby ta praca działała „autonomicznie”? Próbowałem już następujących rzeczy, ale this
nie są one prawidłowe w tym miejscu:
struct Foo
{
typedef decltype(*this) self;
};
// main.cpp:3:22: error: invalid use of 'this' at top level
// typedef decltype(*this) self;
(Nie mam zamiaru martwić się o odpowiednik static
, który robi to samo, ale z późnym wiązaniem).
this_t
byłoby prawdopodobnie bardziej dopasowane do zwykłego nazewnictwa w C ++.auto()
i~auto()
dla lekarzy / lekarzy. Co najmniej interesujące. Może jeśli zostanie użyty do tego celu,typedef auto self;
ale wydaje mi się to trochę szkicowe.decltype(class)
, być może zdecltype(struct)
odpowiednikiem. Jest to o wiele wyraźniejsze niż tylkoauto
w konkretnym kontekście i nie widzę żadnych problemów z dopasowaniem go do języka, na którym się opieradecltype(auto)
.void _check() { static_assert(std::is_same<self&, decltype(*this)>::value, "Correct your self type"); }
Nie działa z szablonami klas ...Odpowiedzi:
Oto, jak możesz to zrobić bez powtarzania typu Foo:
Jeśli chcesz wyprowadzić z
Foo
tego makra, powinieneś użyć makraWITH_SELF_DERIVED
w następujący sposób:Możesz nawet zrobić wielokrotne dziedziczenie z dowolną liczbą klas bazowych (dzięki wariadycznym szablonom i wariadycznym makrom):
Sprawdziłem to, aby działać na gcc 4.8 i clang 3.4.
źródło
auto
idecltype
lub w tym przypadkuself
.template<typename T>class Self{protected: typedef T self;}; class WITH_SELF(Foo) : public Bar, private Baz {};
byłoby prostsze i umożliwiłoby bardziej precyzyjną kontrolę nad dziedziczeniem - czy są powody przeciw?Możliwe obejście (ponieważ nadal musisz raz wpisać typ):
Aby uzyskać bezpieczniejszą wersję, możemy zapewnić, że
T
faktycznie pochodzi zSelf<T>
:Zauważ, że
static_assert
funkcja wewnątrz elementu członkowskiego jest prawdopodobnie jedynym sposobem sprawdzenia, ponieważ przekazywane typystd::is_base_of
muszą być kompletne.źródło
typename
w typedef. A ponieważ nie zmniejsza to liczby zwolnień, nie sądzę, aby była to realna alternatywa.Foo
nazwy.Foo
, musisz albo: (1) propagować T w górę do potomka liścia, albo (2) pamiętać o wielokrotnym dziedziczeniu po SelfT lub (3) zaakceptuj, że wszystkie dzieci uważają za Bazę… użyteczne, ale nieładne.self
a niestatic
, nie stanowi to problemu.Możesz użyć makra zamiast zwykłej deklaracji klasy, która zrobi to za Ciebie.
A potem użyj like
#define END_CLASS };
prawdopodobnie pomogłoby w czytelności.Możesz także wziąć @ Paranaix
Self
i użyć go (zaczyna robić się naprawdę hakerski)źródło
{
, więc}
„zawiesza się”, czego edytorzy tekstu prawdopodobnie by nie lubili.CLASS_WITH_SELF(foo) { … };
- i myślę, że jest to niemożliwe do osiągnięcia.Self
.Nie mam żadnych pozytywnych dowodów, ale myślę, że to niemożliwe. Poniższe zawodzi - z tego samego powodu co twoja próba - i myślę, że to najdalej, co możemy osiągnąć:
Zasadniczo pokazuje to, że zakres, w którym chcemy zadeklarować naszą typedef, po prostu nie ma dostępu (ani bezpośredniego, ani pośredniego) do
this
i nie ma innego (niezależnego od kompilatora) sposobu na dotarcie do typu lub nazwy klasy.źródło
decltype
jest niedocenianym kontekstem, więc wywołanie funkcji członkowskiej nie stanowi problemu (nie zostanie podjęta próba)struct S { int i; typedef decltype(i) Int; };
działa, mimo że niei
jest statycznym składnikiem danych. Działa, ponieważdecltype
ma specjalny wyjątek, w którym prosta nazwa nie jest oceniana jako wyrażenie. Ale nie mogę wymyślić żadnego sposobu wykorzystania tej możliwości w sposób, który odpowiadałby na pytanie.To, co działa zarówno w GCC, jak i clang, polega na utworzeniu typedef, do którego odwołuje się
this
poprzez użyciethis
w końcowym zwracanym typie funkcji typedef. Ponieważ nie jest to deklaracja statycznej funkcji składowej, użyciethis
jest tolerowane. Następnie możesz użyć tego typu do zdefiniowaniaself
.Niestety, ścisła lektura normy mówi, że nawet to nie jest ważne. Clang sprawdza,
this
czy nie jest używany w definicji statycznej funkcji składowej. A tutaj rzeczywiście tak nie jest. GCC nie ma nic przeciwko, jeślithis
jest używane w typie końcowym zwracanym niezależnie od rodzaju funkcji, pozwala na to nawet dlastatic
funkcji składowych. Jednak standard faktycznie wymaga tego, abythis
nie był używany poza definicją niestatycznej funkcji składowej (lub niestatycznego inicjatora składowej danych). Intel ma rację i odrzuca to.Jeśli się uwzględni:
this
jest dozwolone tylko w statycznych inicjatorach składowych danych i niestatycznych funkcjach składowych ([wyr.prim.general] p5),this
można ich użyć ([over.call.func] p3),Myślę, że mogę definitywnie powiedzieć, że nie ma żadnego sposobu na zaimplementowanie
self
bez dołączenia gdzieś nazwy typu.Edycja : W moim wcześniejszym rozumowaniu jest błąd. „Niestatyczne funkcje składowe mogą być wywoływane tylko przez niekwalifikowaną nazwę, nawet w niedocenianych kontekstach, kiedy można tego użyć ([over.call.func] p3)” jest niepoprawne. To, co faktycznie mówi, to
Wewnątrz statycznej funkcji składowej
this
może się nie pojawiać, ale nadal istnieje.Jednak zgodnie z komentarzami wewnątrz statycznej funkcji składowej transformacja
f()
(*this).f()
składowej do nie zostanie wykonana, a jeśli nie zostanie wykonana, naruszone jest [wyrażenie] p1:ponieważ nie byłoby dostępu dla członków. Więc nawet to nie zadziała.
źródło
_self_fn_1()
jest "przekształcany" w(*this)._self_fn_1()
. Nie jestem jednak pewien, czy to czyni to nielegalnym.X
w kontekście, w którymthis
można go użyć”, więc nie sądzę, aby wykonywana była transformacja.auto _self_fn_1() -> decltype(*this); auto _self_fn_1() const -> decltype(*this);
?)const
Jednak o przeciążeniach: to nie jest problem. 5.1p3 został specjalnie zmodyfikowany, aby mieć również zastosowanie do statycznych funkcji składowych i mówi, że typthis
jestFoo*
/Bar*
(bezconst
), ponieważ nie maconst
w deklaracji_self_fn_2
.to nie działa na typach szablonów, ponieważ
self_check
nie jest nazywany, więcstatic_assert
nie jest oceniane.Możemy zrobić kilka hacków, aby działało również przez
template
s, ale ma to niewielki koszt w czasie wykonywania.struct
w klasie tworzony jest pusty bajt o rozmiarze 1. Jeśli tworzony jest typ,self
jest testowany przeciwko.źródło
template
opcjami obsługi klas.inline
. Oznacza to, że nie musiszinline
w ogóle pisać . Więc jeśli pisałeśinline
przed każdą taką definicją funkcji członka klasy przez całą swoją karierę, możesz teraz przestać;)Myślę też, że to niemożliwe, oto kolejna nieudana, ale interesująca próba IMHO, która pozwala uniknąć
this
-dostępu:co kończy się niepowodzeniem, ponieważ C ++ wymaga zakwalifikowania
self_f
się do klasy, jeśli chcesz zająć jej adres :(źródło
int T::*
wskaźnika do zmiennej składowej. Iint self_var; typedef decltype(&self_var) self_ptr
też nie działa, to po prostu normalneint*
.Niedawno odkryłem, że
*this
jest to dozwolone w inicjatorze nawiasów klamrowych lub równych . Opisane w § 5.1.1 ( z projektu roboczego n3337 ):Mając to na uwadze, poniższy kod:
mija Daniel Frey's
static_assert
.Live example
źródło
test
choć= this
, prawda? I dlaczego nie tylkousing self = Foo*;
test
typem, hmFoo *
!Chyba że typ musi być typu członkiem klasy otaczającej można wymienić zastosowanie
self
przydecltype(*this)
. Jeśli używasz go w wielu miejscach w kodzie, możesz zdefiniować makroSELF
w następujący sposób:źródło
self
odnosić się do bezpośrednio otaczającej klasy, a nie do klasy zewnętrznej? Ale ja nie znam tak dobrze PHP.Podaj moją wersję. Najlepsze jest to, że jego użycie jest takie samo jak w klasie natywnej. Jednak nie działa w przypadku klas szablonów.
źródło
Opierając się na odpowiedzi hvd, stwierdziłem, że jedyną rzeczą, której brakowało, było usunięcie odwołania, dlatego sprawdzenie std :: is_same kończy się niepowodzeniem (b / c typ wynikowy jest w rzeczywistości odniesieniem do typu). Teraz to bez parametrów makro może wykonać całą pracę. Przykład roboczy poniżej (używam GCC 8.1.1).
źródło
Powtórzę oczywiste rozwiązanie polegające na „konieczności zrobienia tego samemu”. To jest zwięzła wersja kodu w C ++ 11, która działa zarówno z prostymi klasami, jak i szablonami klas:
Możesz to zobaczyć w akcji w ideone . Geneza prowadząca do tego wyniku jest poniżej:
Ma to oczywisty problem z kopiowaniem i wklejaniem kodu do innej klasy i zapominaniem o zmianie XYZ, jak tutaj:
Moje pierwsze podejście nie było zbyt oryginalne - stworzenie funkcji takiej jak ta:
To trochę długie, ale proszę o wyrozumiałość. Ma to tę zaletę, że pracuje w C ++ 03 bez
decltype
, ponieważ__self_check_helper
funkcja służy do wywnioskowania typuthis
. Nie ma teżstatic_assert
, alesizeof()
zamiast tego stosuje się sztuczkę. Możesz go znacznie skrócić dla C ++ 0x. Teraz to nie zadziała w przypadku szablonów. Istnieje również niewielki problem z makrem, które nie oczekuje średnika na końcu, jeśli kompiluje się z pedantycznym, będzie narzekać na dodatkowy niepotrzebny średnik (lub pozostanie z dziwnie wyglądającym makrem nie kończącym się średnikiem w treściXYZ
iABC
).Sprawdzenie tego,
Type
co jest przekazywane,DECLARE_SELF
nie jest opcją, ponieważ sprawdzałoby tylkoXYZ
klasę (która jest w porządku), nieświadomaABC
(która ma błąd). I wtedy do mnie dotarło. Bezkosztowe rozwiązanie do magazynowania bez dodatkowych kosztów, które współpracuje z szablonami:To po prostu tworzy statyczne potwierdzenie na unikalnej wartości wyliczenia (lub przynajmniej unikalne w przypadku, gdy nie piszesz całego kodu w jednym wierszu), nie jest stosowana żadna sztuczka porównująca typy i działa jako asercja statyczna, nawet w szablonach . I jako bonus - ostatni średnik jest teraz wymagany :).
Chciałbym podziękować Yakkowi za dobrą inspirację. Nie napisałbym tego bez uprzedniego zobaczenia jego odpowiedzi.
Testowane z VS 2008 i g ++ 4.6.3. Rzeczywiście, z przykładem
XYZ
iABC
narzeka:Teraz, jeśli zrobimy ABC szablonem:
Dostaniemy:
Wyzwolono tylko sprawdzenie numeru wiersza, ponieważ kontrola funkcji nie została skompilowana (zgodnie z oczekiwaniami).
W C ++ 0x (i bez złych znaków podkreślenia) wystarczyłoby:
Uważam, że bit CStaticAssert jest niestety nadal wymagany, ponieważ tworzy typ, który jest wpisany w treści szablonu (przypuszczam, że tego samego nie można zrobić
static_assert
). Zaletą tego podejścia jest nadal zerowy koszt.źródło
static_assert
tutaj ponownie , prawda? Ponadto cały kod jest nieprawidłowy, ponieważ używasz nielegalnych (zastrzeżonych) identyfikatorów.Nie wiem wszystkiego o tych zwariowanych szablonach, a może coś super prostego:
Praca wykonana, chyba że nie możesz znieść kilku makr. Możesz nawet użyć
CLASSNAME
do zadeklarowania konstruktora (-ów) (i oczywiście destruktora).Demo na żywo .
źródło