Pertimbangkan program berikut:
struct ghost
{
// ghosts like to pretend that they don't exist
ghost* operator&() const volatile { return 0; }
};
int main()
{
ghost clyde;
ghost* clydes_address = &clyde; // darn; that's not clyde's address :'(
}
Bagaimana cara saya mendapatkan clyde
alamat?
Saya mencari solusi yang akan bekerja sama baiknya untuk semua jenis objek. Solusi C ++ 03 akan lebih baik, tetapi saya juga tertarik pada solusi C ++ 11. Jika memungkinkan, mari kita hindari perilaku spesifik implementasi.
Saya mengetahui std::addressof
templat fungsi C ++ 11 , tetapi saya tidak tertarik menggunakannya di sini: Saya ingin memahami bagaimana implementor Perpustakaan Standar dapat mengimplementasikan templat fungsi ini.
c++
c++11
operator-overloading
memory-address
James McNellis
sumber
sumber
:)
)CComPtr<>
danCComQIPtr<>
kelebihan bebanoperator&
Jawaban:
Pembaruan: di C ++ 11, orang dapat menggunakan
std::addressof
sebagai gantiboost::addressof
.Pertama-tama mari kita salin kode dari Boost, minus pekerjaan kompiler di sekitar bit:
Catatan:
addressof
tidak dapat digunakan dengan pointer berfungsiDalam C ++ jika
void func();
dideklarasikan, makafunc
adalah referensi ke fungsi tanpa argumen dan tidak mengembalikan hasil. Referensi ke fungsi ini dapat secara sepele dikonversi menjadi pointer ke fungsi - dari@Konstantin
: Menurut 13.3.3.2 keduanyaT &
danT *
tidak dapat dibedakan untuk fungsi. Yang pertama adalah konversi Identity dan yang kedua adalah konversi Function-to-Pointer yang keduanya memiliki peringkat "Pencocokan Tepat" (13.3.3.1.1 tabel 9).The referensi ke fungsi melewati
addr_impl_ref
, ada ambiguitas dalam resolusi yang berlebihan untuk pilihanf
, yang diselesaikan berkat argumen boneka0
, yang merupakanint
pertama dan bisa dipromosikan kelong
(Konversi Integral).Jadi kita cukup mengembalikan pointer.
Jika operator konversi menghasilkan a
T*
maka kami memiliki ambiguitas: untukf(T&,long)
Promosi Integral diperlukan untuk argumen kedua sedangkan untukf(T*,int)
operator konversi dipanggil pada yang pertama (terima kasih kepada @litb)Saat itulah
addr_impl_ref
masuk. Standar C ++ mengamanatkan bahwa urutan konversi dapat berisi paling banyak satu konversi yang ditentukan pengguna. Dengan memasukkan jenisaddr_impl_ref
dan memaksa penggunaan urutan konversi, kami "menonaktifkan" semua operator konversi yang disertakan.Dengan demikian
f(T&,long)
kelebihan dipilih (dan Promosi Integral dilakukan).Dengan demikian
f(T&,long)
kelebihan dipilih, karena jenisnya tidak sesuai denganT*
parameter.Catatan: dari keterangan dalam file tentang kompatibilitas Borland, array tidak membusuk ke pointer, tetapi diteruskan dengan referensi.
Kami ingin menghindari penerapan
operator&
ke jenis, karena mungkin telah kelebihan beban.Standar menjamin yang
reinterpret_cast
dapat digunakan untuk pekerjaan ini (lihat jawaban @Matteo Italia: 5.2.10 / 10).Boost menambahkan beberapa fitur
const
danvolatile
kualifikasi untuk menghindari peringatan penyusun (dan menggunakan aconst_cast
untuk menghapusnya dengan benar).T&
kechar const volatile&
const
danvolatile
&
operator untuk mengambil alamatnyaT*
The
const
/volatile
juggling adalah sedikit ilmu hitam, tetapi tidak menyederhanakan pekerjaan (daripada memberikan 4 overload). Perhatikan bahwa sejakT
adalah wajar tanpa pengecualian, jika kita lulusghost const&
, makaT*
adalahghost const*
, dengan demikian kualifikasi belum benar-benar hilang.EDIT: pointer overload digunakan untuk fungsi pointer, saya sedikit mengubah penjelasan di atas. Saya masih tidak mengerti mengapa itu perlu .
Output ideone berikut merangkum ini, agak.
sumber
f
overloads di mana templat fungsi, sedangkan mereka adalah fungsi anggota biasa dari kelas templat, terima kasih telah menunjukkannya. (Sekarang saya hanya perlu mencari tahu apa gunanya kelebihan, tip?)char*
." Terima kasih, Matthieu.T*
? EDIT: Sekarang saya mengerti. Memang, tapi dengan0
argumen itu akan berakhir di salib-silang , jadi akan ambigu.Gunakan
std::addressof
.Anda dapat menganggapnya sebagai berikut di belakang layar:
Implementasi yang ada (termasuk Boost.Addressof) melakukan hal itu, hanya menjaga
const
danvolatile
kualifikasi tambahan.sumber
Trik di belakang
boost::addressof
dan implementasi yang disediakan oleh @Luc Danton bergantung pada keajaibanreinterpret_cast
; standar secara eksplisit menyatakan pada §5.2.10 ¶10 ituSekarang, ini memungkinkan kita untuk mengkonversi referensi objek arbitrer ke a
char &
(dengan kualifikasi cv jika referensi tersebut memenuhi syarat cv), karena setiap pointer dapat dikonversi menjadi (mungkin memenuhi syarat cv)char *
. Sekarang kita memilikichar &
, operator kelebihan pada objek tidak lagi relevan, dan kita dapat memperoleh alamat dengan&
operator builtin .Implementasi boost menambahkan beberapa langkah untuk bekerja dengan objek yang memenuhi syarat cv: yang pertama
reinterpret_cast
dilakukanconst volatile char &
, jikachar &
gips polos tidak akan bekerja untukconst
dan / atauvolatile
referensi (reinterpret_cast
tidak dapat menghapusconst
). Kemudianconst
danvolatile
dihapus denganconst_cast
, alamat diambil dengan&
, dan finalreinterpet_cast
untuk tipe "benar" dilakukan.The
const_cast
diperlukan untuk menghapusconst
/volatile
yang bisa telah ditambahkan ke non-const / referensi volatile, tapi tidak "bahaya" apa adalahconst
/volatile
referensi di tempat pertama, karena finalreinterpret_cast
akan kembali add-cv-kualifikasi apakah itu ada di tempat pertama (reinterpret_cast
tidak bisa menghapusconst
tetapi bisa menambahkannya).Adapun sisa kode dalamaddressof.hpp
, tampaknya sebagian besar adalah untuk penyelesaian masalah. Thestatic inline T * f( T * v, int )
tampaknya diperlukan hanya untuk compiler Borland, namun kehadirannya memperkenalkan kebutuhanaddr_impl_ref
, jika jenis pointer akan ditangkap oleh kelebihan kedua ini.Sunting : berbagai kelebihan memiliki fungsi yang berbeda, lihat @Matthieu M. jawaban yang sangat baik .Yah, saya tidak lagi yakin akan hal ini juga; Saya harus menyelidiki lebih lanjut kode itu, tapi sekarang saya sedang memasak makan malam :), saya akan melihatnya nanti.
sumber
void func();
boost::addressof(func);
. Namun menghapus kelebihan tidak mencegah gcc 4.3.4 dari mengkompilasi kode dan menghasilkan output yang sama, jadi saya masih tidak mengerti mengapa perlu untuk memiliki kelebihan ini.Saya telah melihat implementasi dari
addressof
melakukan ini:Jangan tanya saya bagaimana menyesuaikannya!
sumber
char*
adalah pengecualian yang tercantum untuk mengetikkan aturan aliasing.reinterpret_cast<char*>
didefinisikan dengan baik.[unsigned] char *
dan dengan demikian membaca representasi objek dari objek yang ditunjuk. Ini adalah area lain di manachar
memiliki hak istimewa khusus.Lihatlah boost :: addressof dan implementasinya.
sumber
addressof
mengembalikan pointer itu sendiri. Dapat diperdebatkan apakah itu yang diinginkan atau tidak oleh pengguna, tetapi bagaimana cara yang ditentukan.addr_impl_ref
, jadi pointer yang berlebihan tidak boleh disebut ...