Apakah Anda menggunakan boost::variant
atau salah satu dari banyak implementasi C ++ 11 open-source dari "Union Tagged" atau jenis varian dalam proyek C ++ Anda?
boost::variant
adalah perpustakaan yang hebat. Saya membuat strict_variant
untuk mengatasi beberapa hal tentang boost::variant
yang tidak saya sukai.
Versi TL; DR tidak seperti boost::variant
atau std::variant
, strict_variant
tidak akan pernah melempar pengecualian atau membuat alokasi dinamis dalam upaya jenis pendukung yang memiliki gerakan melempar. Versi default hanya akan gagal menegaskan statis jika ini akan terjadi. strict_variant::easy_variant
akan membuat alokasi dalam situasi ini, sehingga Anda dapat memilihnya jika Anda mau, dan dua versi varian ini "bermain dengan baik" bersama-sama. Hal semacam ini sering menjadi perhatian utama dalam proyek dengan persyaratan waktu nyata, atau di perangkat tertanam, yang mungkin tidak memungkinkan, atau mungkin tidak memiliki fitur C ++ ini. Jika Anda membuat perpustakaan yang mungkin digunakan dalam proyek "konvensional" yang menginginkan kemudahan penggunaan yang berasal dari boost::variant
, tetapi mungkin juga digunakan dalam proyek dengan persyaratan terbatas, dan Anda ingin menggunakan jenis variant
Sebagai bagian dari API, strict_variant
mungkin menawarkan cara untuk membuat semua orang bahagia.
Selain itu, ada beberapa masalah dalam antarmuka varian yang ditangani yang membuatnya lebih menyenangkan untuk menggunakan IMHO sehari-hari. (Ini sebenarnya motivasi asli dari proyek.)
Saya tidak suka kode seperti ini dapat dikompilasi tanpa peringatan atau pesan kesalahan:
boost::variant<std::string, int > v;
v = true ;
Saya biasanya lebih suka bahwa variant
saya lebih membatasi tentang apa konversi implisit dapat terjadi.
Saya ingin hal -hal seperti ini harus dikompilasi dan melakukan apa yang masuk akal, bahkan jika resolusi yang berlebihan akan ambigu.
variant< bool , long , double , std::string> v;
v = true ; // selects bool
v = 10 ; // selects long
v = 20 . 5f ; // selects double
v = " foo " ; // selects string
Saya juga ingin bahwa perilaku seperti itu (apa yang dipilih dalam kasus seperti itu) adalah portabel.
(Untuk contoh kode seperti ini, di mana boost::variant
memiliki perilaku yang tidak menguntungkan, lihat "Abstrak dan Motivasi" dalam dokumentasi.)
Dalam strict_variant
kami memodifikasi resolusi kelebihan beban dalam situasi ini dengan menghapus beberapa kandidat.
Misalnya:
bool
, integral, titik mengambang, pointer, karakter, dan beberapa kelas lainnya.int -> long
dan int -> long long
adalah kandidat, long long
dihilangkan.Lihat dokumentasi untuk detailnya.
Saya tidak suka boost::variant
akan diam -diam membuat salinan cadangan dari objek saya. Misalnya, pertimbangkan program sederhana ini, di mana A
dan B
telah didefinisikan untuk mencatat semua panggilan CTOR dan DTOR.
int main () {
using var_t = boost::variant<A, B>;
var_t v{ A ()};
std::cout << " 1 " << std::endl;
v = B ();
std::cout << " 2 " << std::endl;
v = A ();
std::cout << " 3 " << std::endl;
}
boost::variant
menghasilkan output berikut:
A ()
A(A&&)
~A()
1
B()
B(B&&)
A( const A &)
~A()
B( const B &)
~A()
~B()
~B()
2
A()
A(A&&)
B( const B &)
~B()
A( const A &)
~B()
~A()
~A()
3
~A()
Ini mungkin cukup mengejutkan bagi beberapa programmer.
Sebaliknya, jika Anda menggunakan C ++ 17 std::variant
, atau salah satu varian dengan semantik "terkadang-kosong", Anda mendapatkan sesuatu seperti ini (output ini dari std::experimental::variant
)
A ()
A(A&&)
~A()
1
B()
~A()
B(B&&)
~B()
2
A()
~B()
A(A&&)
~A()
3
~A()
Ini jauh lebih dekat dengan apa yang diharapkan oleh programmer naif siapa yang tidak tahu tentang detail internal boost::variant
- satu -satunya salinan objeknya yang ada adalah apa yang dapat dilihatnya dalam kode sumbernya.
Hal semacam ini biasanya tidak masalah, tetapi kadang -kadang jika misalnya Anda men -debug masalah korupsi memori yang buruk (mungkin ada kode buruk di salah satu objek yang terkandung dalam varian), maka objek ekstra ini, bergerak, dan salinan, mungkin membuat hal -hal yang kebetulan lebih rumit.
Inilah yang Anda dapatkan dengan strict_variant
:
A ()
A(A&&)
~A()
1
B()
B(B&&)
~A()
~B()
2
A()
A(A&&)
~B()
~A()
3
~A()
Namun, strict_variant
tidak memiliki keadaan kosong, dan sepenuhnya aman!
(Contoh -contoh ini dari gcc 5.4
, lihat kode di folder example
.)
Untuk meringkas perbedaan:
std::variant
jarang kosong, selalu berbasis tumpukan. Bahkan, itu kosong persis saat pengecualian dilemparkan. Kemudian, ia melempar pengecualian yang berbeda jika Anda mencoba mengunjungi saat kosong.boost::variant
tidak pernah kosong, biasanya berbasis tumpukan. Itu harus membuat alokasi dinamis dan salinan cadangan setiap kali pengecualian dapat dilemparkan, tetapi itu akan dibebaskan setelah jika pengecualian tidak benar -benar dilemparkan.strict_variant
tidak pernah kosong, dan berbasis stack tepat ketika tipe nilai saat ini tidak dapat dipindahkan. Itu tidak pernah membuat cadangan bergerak atau menyalin, dan tidak pernah melempar pengecualian. Setiap pendekatan memiliki kelebihan. Saya memilih pendekatan strict_variant
karena saya merasa lebih sederhana dan menghindari apa yang saya anggap sebagai kelemahan dari boost::variant
dan std::variant
. Dan, strict_variant
std::variant
berhasil membuat semua tipe Anda tidak dapat dilemparkan.
Untuk diskusi mendalam tentang desain, lihat dokumentasi.
Untuk intro yang lembut ke varian, dan gambaran umum variant yang ketat, lihat slide dari pembicaraan yang saya berikan tentang ini: [pptx] [pdf]
Di halaman GitHub.
strict_variant
menargetkan standar C ++ 11.
Ia diketahui bekerja dengan gcc >= 4.8
dan clang >= 3.5
, dan diuji terhadap MSVC 2015
.
strict_variant
dapat digunakan apa adanya dalam proyek-proyek yang membutuhkan -fno-exceptions
dan -fno-rtti
.
Varian ketat tersedia di bawah lisensi Boost Software.