
Ringkasan bagian sebelumnya
Karena pembatasan pada kemampuan untuk menggunakan kompiler C ++ 11, dan dari kurangnya alternatif, dorongan ingin menulis implementasi sendiri dari pustaka C ++ 11 standar di atas pustaka C ++ 98 / C ++ 03 yang disertakan dengan kompiler.
Static_assert ,
noexcept ,
countof diimplementasikan, dan juga, setelah mempertimbangkan semua definisi non-standar dan fitur kompiler, informasi muncul tentang fungsi yang didukung oleh kompiler saat ini. Implementasinya sendiri dari
nullptr disertakan , yang dipilih pada tahap kompilasi.
Waktunya telah tiba untuk
type_traits dan semua "keajaiban templat khusus" ini.
Tautan ke GitHub dengan hasil untuk hari ini untuk yang tidak sabar dan yang bukan pembaca:
Komitmen dan kritik yang membangun dipersilahkan
Benamkan diri Anda di dunia "templat ajaib" C ++.
Daftar isi
PendahuluanBab 1. Viam supervadet vadensBab 2. #ifndef __CPP11_SUPPORT__ #define __COMPILER_SPECIFIC_BUILT_IN_AND_MACRO_HELL__ #endifBab 3. Menemukan implementasi nullptr yang sempurnaBab 4. C ++ Template Magic....
4.1 Kita mulai dari yang kecil....
4.2 Tentang berapa banyak kesalahan ajaib yang dikompilasi oleh log untuk kita....
4.3 Pointer dan semuanya....
4.4 Apa lagi yang diperlukan untuk pustaka templatBab 5
...
Bab 4. C ++ Template Magic
Setelah selesai dengan kata kunci C ++ 11 dan semua "sakelar" yang bergantung pada definisi di antara implementasinya, saya mulai mengisi
type_traits . Sebenarnya, saya sudah memiliki beberapa kelas template, mirip dengan yang standar, yang sudah bekerja dalam proyek untuk waktu yang lama dan karena itu tetap membawa semua ini ke dalam bentuk yang sama, serta menambahkan fungsi yang hilang.

Jujur, saya terinspirasi oleh pemrograman template. Terutama kesadaran bahwa semua ini adalah berbagai pilihan: perhitungan, percabangan kode, kondisi, pengecekan kesalahan dilakukan selama proses kompilasi dan tidak ada biaya program akhir saat dijalankan. Dan karena templat dalam C ++ pada dasarnya adalah
bahasa pemrograman Turing-complete , saya mengantisipasi betapa elegan dan relatif mudahnya mungkin untuk mengimplementasikan bagian standar yang terkait dengan pemrograman pada templat. Tetapi, untuk segera menghancurkan semua ilusi, saya akan mengatakan bahwa seluruh teori kelengkapan Turing dipecah menjadi implementasi konkret templat dalam kompiler. Dan bagian penulisan perpustakaan ini, bukannya solusi elegan dan "trik" pemrograman template, berubah menjadi perjuangan sengit dengan kompiler, sementara masing-masing "runtuh" dengan caranya sendiri, dan ada baiknya jika masuk ke kesalahan kompiler internal yang cadel, atau bahkan jatuh dengan keras dengan pengecualian tidak tertangani. GCC (g ++) menunjukkan dirinya sendiri yang terbaik, yang dengan tenang “mengunyah” semua konstruksi templat dan hanya dikutuk (dalam kasus ini) di tempat-tempat di mana tidak ada nama
ketik yang eksplisit.
4.1 Mulai dari yang kecil
Saya mulai dengan template sederhana untuk
std :: integral_constant ,
std :: bool_constant dan template kecil serupa.
template<class _Tp, _Tp Val> struct integral_constant {
Berdasarkan pada
kondisi, Anda dapat memasukkan templat yang sesuai untuk operasi logis {"dan", "atau", "tidak"} lebih dari tipe (Dan semua operasi ini dianggap tepat pada tahap kompilasi! Ini hebat, bukan?):
namespace detail { struct void_type {};
Tiga hal yang patut diperhatikan di sini:
1) Penting untuk selalu menempatkan spasi di antara kurung sudut ('<' dan '>') dari templat, karena sebelum C ++ 11 tidak ada klarifikasi dalam standar tentang cara menafsirkan '>>' dan '<<' dalam kode seperti _atau _ <_ B2, _atau _ <_ B3, _B4 >> , dan karenanya hampir semua kompiler memperlakukan ini sebagai operator bit shift, yang mengarah ke kesalahan kompilasi.
2) Dalam beberapa kompiler (Visual Studio 6.0 misalnya) ada bug yang mengandung fakta bahwa tidak mungkin untuk menggunakan tipe void sebagai parameter templat. Untuk tujuan ini, tipe void_type yang terpisah diperkenalkan pada bagian di atas untuk menggantikan tipe void di mana nilai parameter templat default diperlukan.
3) Kompiler yang sangat lama (Borland C ++ Builder misalnya) memiliki tipe bool yang diimplementasikan secara bengkok , yang dalam beberapa situasi "tiba-tiba" berubah menjadi int ( true -> 1, false -> 0), serta jenis variabel statis konstan dari tipe tersebut. bool (dan bukan hanya mereka), jika mereka terkandung dalam kelas template. Karena semua kekacauan ini, sebagai akibatnya, untuk perbandingan yang benar-benar tidak berbahaya dalam gaya my_template_type :: static_bool_value == false, kompiler dapat dengan mudah mengeluarkan sebuah mempesona tidak dapat melemparkan 'tipe tidak terdefinisi' ke int (0) atau sesuatu seperti itu. Oleh karena itu, perlu untuk selalu mencoba untuk secara eksplisit menunjukkan jenis nilai untuk perbandingan, dengan demikian membantu kompiler menentukan jenis mana yang berhadapan dengannya.
Tambahkan lebih banyak pekerjaan dengan nilai
const dan
volatile . Pertama,
remove_ yang diterapkan secara sepele ... di mana kami hanya mengkhususkan templat untuk pengubah jenis tertentu - jika jenis dengan pengubah masuk ke dalam templat, kompiler harus, setelah melihat semua spesialisasi (ingat prinsip SFINAE dari
bab sebelumnya ) dari templat, pilih yang paling cocok (dengan indikasi eksplisit dari pengubah yang diinginkan) :
template<class _Tp> struct is_function; template<class _Tp> struct remove_const {
Dan kemudian kita menerapkan template
add_ ... di mana semuanya sudah sedikit lebih rumit:
namespace detail { template<class _Tp, bool _IsFunction> struct _add_const_helper { typedef _Tp const type; }; template<class _Tp> struct _add_const_helper<_Tp, true> { typedef _Tp type; }; template<class _Tp, bool _IsFunction> struct _add_volatile_helper { typedef _Tp volatile type; }; template<class _Tp> struct _add_volatile_helper<_Tp, true> { typedef _Tp type; }; template<class _Tp, bool _IsFunction> struct _add_cv_helper { typedef _Tp const volatile type; }; template<class _Tp> struct _add_cv_helper<_Tp, true> { typedef _Tp type; }; }
Di sini kami hati-hati memproses jenis referensi secara terpisah agar tidak kehilangan tautan. Selain itu, kami tidak akan lupa tentang jenis fungsi yang tidak mungkin untuk membuat
volatile atau
const pada prinsipnya, oleh karena itu kami akan membiarkannya “apa adanya”. Saya dapat mengatakan bahwa semua ini terlihat sangat sederhana, tetapi inilah yang terjadi ketika "iblis ada dalam rincian", atau lebih tepatnya, "bug ada dalam detail implementasi."
Akhir dari bagian pertama dari bab keempat. Pada bagian
kedua saya akan berbicara tentang bagaimana pemrograman template keras diberikan kepada kompiler, dan juga akan ada templat magic yang lebih keren. Ah, namun - mengapa
lama tidak
konstanta integral, menurut beberapa kompiler sampai hari ini.
Terima kasih atas perhatian anda