C ++ tidak memiliki padanan dengan self
kata kunci PHP , yang mengevaluasi jenis kelas penutup.
Cukup mudah untuk memalsukannya berdasarkan kelas:
struct Foo
{
typedef Foo self;
};
tapi saya harus menulis Foo
lagi. Mungkin saya akan mendapatkan kesalahan ini suatu hari dan menyebabkan bug diam.
Dapatkah saya menggunakan kombinasi dari decltype
dan teman untuk membuat ini berfungsi "secara mandiri"? Saya sudah mencoba yang berikut ini tetapi this
tidak valid di tempat itu:
struct Foo
{
typedef decltype(*this) self;
};
// main.cpp:3:22: error: invalid use of 'this' at top level
// typedef decltype(*this) self;
(Saya tidak akan khawatir tentang padanannya static
, yang melakukan hal yang sama tetapi dengan pengikatan yang terlambat.)
this_t
mungkin lebih selaras dengan penamaan C ++ biasa.auto()
dan~auto()
untuk pemberi pinjaman. Menarik untuk sedikitnya. Jika digunakan untuk tujuan itu, mungkintypedef auto self;
, tapi bagi saya itu agak samar.decltype(class)
, mungkin dengandecltype(struct)
padanan. Itu jauh lebih jelas daripada hanyaauto
dalam konteks tertentu dan saya tidak melihat ada masalah dengan itu sesuai dengan bahasa berdasarkandecltype(auto)
.void _check() { static_assert(std::is_same<self&, decltype(*this)>::value, "Correct your self type"); }
Tidak bekerja dengan templat kelas, meskipun ...Jawaban:
Inilah cara Anda melakukannya tanpa mengulangi jenis Foo:
Jika Anda ingin menurunkannya
Foo
maka Anda harus menggunakan makroWITH_SELF_DERIVED
dengan cara berikut:Anda bahkan dapat melakukan banyak pewarisan dengan kelas dasar sebanyak yang Anda inginkan (berkat template variadic dan makro variadic):
Saya telah memverifikasi ini untuk bekerja pada gcc 4.8 dan clang 3.4.
sumber
auto
dandecltype
atau dalam kasus iniself
.template<typename T>class Self{protected: typedef T self;}; class WITH_SELF(Foo) : public Bar, private Baz {};
akan lebih sederhana dan akan memungkinkan kontrol yang lebih tepat atas warisan - ada alasan yang menentang?Solusi yang mungkin (karena Anda masih harus menulis jenisnya sekali):
Untuk versi yang lebih aman, kami dapat memastikan bahwa
T
sebenarnya berasal dariSelf<T>
:Perhatikan bahwa
static_assert
di dalam fungsi anggota mungkin satu-satunya cara untuk memeriksa, karena tipe yang diteruskanstd::is_base_of
harus lengkap.sumber
typename
di typedef. Dan karena ini tidak mengurangi jumlah redundansi, menurut saya ini bukan alternatif yang layak.Foo
nama yang persis sama .Foo
, Anda harus: (1) menyebarkan T ke atas ke keturunan daun, atau (2) ingat untuk mewarisi dari SelfT berkali-kali , atau (3) menerima bahwa semua hal anak-anak menjadi Base .. dapat digunakan, tetapi bersahaja.self
daripadastatic
, itu tidak masalah.Anda dapat menggunakan makro sebagai ganti deklarasi kelas biasa, yang akan melakukannya untuk Anda.
Dan kemudian gunakan seperti
#define END_CLASS };
mungkin akan membantu keterbacaan.Anda juga dapat menggunakan @ Paranaix
Self
dan menggunakannya (mulai menjadi sangat hack)sumber
{
, jadi}
is "hang", yang mungkin tidak disukai oleh editor teks juga.CLASS_WITH_SELF(foo) { … };
- dan saya pikir itu tidak mungkin dicapai.Self
dimilikinya.Saya tidak memiliki bukti positif tetapi saya pikir itu tidak mungkin. Berikut ini gagal - karena alasan yang sama dengan upaya Anda - dan saya pikir itu yang terjauh yang bisa kita dapatkan:
Pada dasarnya, apa ini menunjukkan bahwa ruang lingkup di mana kita ingin mendeklarasikan typedef kami hanya memiliki tidak akses (baik itu langsung maupun tidak langsung) untuk
this
, dan tidak ada lainnya (compiler independen) cara untuk mendapatkan dengan jenis kelas atau nama.sumber
decltype
adalah konteks yang tidak dievaluasi, jadi memanggil fungsi anggota bukanlah masalah (yang tidak akan dicoba)struct S { int i; typedef decltype(i) Int; };
berfungsi meskipuni
merupakan anggota data non-statis. Ini berfungsi karenadecltype
memiliki pengecualian khusus di mana nama sederhana tidak dievaluasi sebagai ekspresi. Tetapi saya tidak dapat memikirkan cara apa pun untuk menggunakan kemungkinan ini dengan cara menjawab pertanyaan itu.Apa yang berfungsi baik di GCC dan clang adalah membuat typedef yang merujuk ke
this
dengan menggunakanthis
tipe trailing-return dari fungsi typedef. Karena ini bukan deklarasi fungsi anggota statis, penggunaan darithis
ditoleransi. Anda kemudian dapat menggunakan typedef itu untuk mendefinisikanself
.Sayangnya, pembacaan standar yang ketat mengatakan bahwa ini pun tidak valid. Apa yang dilakukan clang adalah pemeriksaan yang
this
tidak digunakan dalam definisi fungsi anggota statis. Dan di sini, memang tidak. GCC tidak keberatan jikathis
digunakan dalam jenis kembali-akhir apa pun jenis fungsinya, GCC mengizinkannya bahkan untukstatic
fungsi anggota. Namun, apa yang sebenarnya dibutuhkan standar adalah yangthis
tidak digunakan di luar definisi fungsi anggota non-statis (atau inisialisasi anggota data non-statis). Intel melakukannya dengan benar dan menolak ini.Mengingat bahwa:
this
hanya diperbolehkan dalam penginisialisasi anggota data non-statis dan fungsi anggota non-statis ([expr.prim.general] p5),this
dapat digunakan ([over.call.func] p3),Saya rasa saya dapat secara meyakinkan mengatakan bahwa tidak ada cara sama sekali untuk mengimplementasikan
self
tanpa memasukkan dalam beberapa cara, di suatu tempat, nama tipe.Sunting : Ada kekurangan dalam alasan saya sebelumnya. "Fungsi anggota non-statis hanya dapat dipanggil dengan nama yang tidak memenuhi syarat, bahkan dalam konteks yang tidak dievaluasi, saat ini dapat digunakan ([over.call.func] p3)," salah. Apa yang sebenarnya dikatakannya adalah
Di dalam fungsi anggota statis,
this
mungkin tidak muncul, tetapi masih ada.Namun, berdasarkan komentar, di dalam fungsi anggota statis, transformasi
f()
menjadi(*this).f()
tidak akan dilakukan, dan itu tidak dilakukan, maka [expr.call] p1 dilanggar:karena tidak akan ada akses anggota. Jadi itu pun tidak akan berhasil.
sumber
_self_fn_1()
"diubah" menjadi(*this)._self_fn_1()
. Tidak yakin apakah itu membuatnya ilegal.X
dalam konteks di manathis
dapat digunakan", jadi saya tidak berpikir bahwa transformasi dilakukan.auto _self_fn_1() -> decltype(*this); auto _self_fn_1() const -> decltype(*this);
?)const
kelebihan beban, meskipun: itu bukan masalah. 5.1p3 secara khusus telah dimodifikasi untuk juga diterapkan pada fungsi anggota statis, dan menyatakan tipethis
isFoo*
/Bar*
(tanpaconst
), karena tidak adaconst
dalam deklarasi_self_fn_2
.ini tidak bekerja pada jenis template, karena
self_check
tidak dipanggil, jadistatic_assert
tidak dievaluasi.Kami dapat melakukan beberapa peretasan untuk membuatnya berfungsi
template
juga, tetapi memiliki biaya waktu pengoperasian yang kecil.ruang kosong
struct
berukuran 1 byte dibuat di kelas Anda. Jika tipe Anda dibuat instance-nya,self
diuji terhadap.sumber
template
opsi dukungan kelas.inline
,. Artinya Anda tidak perlu menulisinline
sama sekali. Jadi jika Anda telah menulisinline
di depan setiap definisi fungsi anggota kelas untuk seluruh karir Anda, Anda dapat berhenti sekarang;)Saya juga berpikir itu tidak mungkin, inilah upaya menarik lainnya yang gagal tetapi IMHO yang menghindari
this
-akses:yang gagal karena C ++ mengharuskan Anda untuk memenuhi syarat
self_f
dengan kelas ketika Anda ingin mengambil alamatnya :(sumber
int T::*
penunjuk biasa ke variabel anggota. Danint self_var; typedef decltype(&self_var) self_ptr
tidak berhasil juga, itu hanya biasaint*
.Saya baru-baru ini menemukan bahwa
*this
diperbolehkan dalam brace-or-equal-initializer . Dijelaskan dalam § 5.1.1 ( dari draft kerja n3337 ):Dengan mengingat hal itu, kode berikut:
melewati Daniel Frey
static_assert
.Live example
sumber
test
meskipun= this
, bukan? Dan mengapa tidak hanyausing self = Foo*;
test
menjadi tipe, um,Foo *
!Kecuali jika tipe tersebut harus merupakan tipe anggota dari kelas yang melingkupi, Anda dapat mengganti penggunaannya
self
dengandecltype(*this)
. Jika Anda menggunakannya di banyak tempat di kode Anda, Anda dapat menentukan makroSELF
sebagai berikut:sumber
self
mengacu pada kelas yang langsung melingkupi dan bukan kelas luar? Tapi saya tidak tahu php dengan baik.Berikan versi saya. Yang terbaik adalah penggunaannya sama dengan kelas native. Namun, ini tidak berfungsi untuk kelas template.
sumber
Berdasarkan jawaban dari hvd, saya menemukan bahwa satu-satunya hal yang hilang adalah menghapus referensi, itulah sebabnya pemeriksaan std :: is_same gagal (b / c tipe yang dihasilkan sebenarnya adalah referensi ke tipe). Sekarang makro tanpa parameter ini dapat melakukan semua pekerjaan. Contoh kerja di bawah ini (saya menggunakan GCC 8.1.1).
sumber
Saya akan mengulangi solusi yang jelas "harus melakukannya sendiri". Ini adalah versi kode C ++ 11 yang ringkas, yang berfungsi dengan kelas sederhana dan template kelas:
Anda dapat melihatnya beraksi di ideone . Asal, yang mengarah ke hasil ini di bawah:
Ini memiliki masalah yang jelas dengan menyalin-menempel kode ke kelas yang berbeda dan lupa mengubah XYZ, seperti di sini:
Pendekatan pertama saya tidak terlalu orisinal - membuat fungsi, seperti ini:
Agak panjang, tapi tolong bersabarlah dengan saya di sini. Ini memiliki keuntungan bekerja di C ++ 03 tanpa
decltype
, karena__self_check_helper
fungsi tersebut digunakan untuk menyimpulkan jenisthis
. Juga, tidak adastatic_assert
, tetapisizeof()
trik yang digunakan sebagai gantinya. Anda bisa membuatnya lebih pendek untuk C ++ 0x. Sekarang ini tidak akan berfungsi untuk template. Juga, ada masalah kecil dengan makro yang tidak mengharapkan titik koma di akhir, jika dikompilasi dengan pedantic, itu akan mengeluh tentang titik koma ekstra yang tidak perlu (atau Anda akan ditinggalkan dengan makro yang tampak aneh tidak berakhir dengan titik koma di badanXYZ
danABC
).Melakukan pemeriksaan pada
Type
yang diteruskanDECLARE_SELF
bukanlah suatu pilihan, karena itu hanya akan memeriksaXYZ
kelas (yang ok), tidak menyadariABC
(yang memiliki kesalahan). Dan kemudian aku tersadar. Solusi penyimpanan tanpa biaya tambahan yang bekerja dengan template:Ini hanya membuat pernyataan statis pada nilai enum unik (atau setidaknya unik jika Anda tidak menulis semua kode Anda pada satu baris), tidak ada tipu daya pembanding tipe yang digunakan, dan berfungsi sebagai pernyataan statis, bahkan di template . Dan sebagai bonus - titik koma terakhir sekarang diperlukan :).
Saya ingin berterima kasih kepada Yakk karena telah memberi saya inspirasi yang baik. Saya tidak akan menulis ini tanpa melihat jawabannya terlebih dahulu.
Diuji dengan VS 2008 dan g ++ 4.6.3. Memang, dengan contoh
XYZ
danABC
itu mengeluh:Sekarang jika kita membuat template ABC:
Kita akan mendapatkan:
Hanya pemeriksaan nomor baris yang dipicu, karena pemeriksaan fungsi tidak dikompilasi (seperti yang diharapkan).
Dengan C ++ 0x (dan tanpa garis bawah jahat), Anda hanya perlu:
Saya percaya bahwa bit CStaticAssert sayangnya masih diperlukan karena menghasilkan sebuah tipe, yang diketikkan dalam tubuh template (saya kira hal yang sama tidak dapat dilakukan dengan
static_assert
). Keuntungan dari pendekatan ini masih biaya nolnya.sumber
static_assert
sini, bukan? Selain itu, kode lengkap Anda tidak valid karena Anda menggunakan pengenal ilegal (dicadangkan).Saya tidak tahu semua tentang template aneh ini, bagaimana dengan sesuatu yang sangat sederhana:
Pekerjaan selesai, kecuali Anda tidak tahan dengan beberapa makro. Anda bahkan dapat menggunakan
CLASSNAME
untuk mendeklarasikan konstruktor Anda (dan, tentu saja, destruktor).Demo langsung .
sumber