Saya membaca dokumentasi std::experimental::optional
dan saya punya ide bagus tentang apa fungsinya, tetapi saya tidak mengerti kapan saya harus menggunakannya atau bagaimana saya harus menggunakannya. Situs ini belum berisi contoh apa pun yang membuat saya lebih sulit memahami konsep sebenarnya dari objek ini. Kapan std::optional
pilihan yang baik untuk digunakan, dan bagaimana cara mengompensasi apa yang tidak ditemukan dalam Standar sebelumnya (C ++ 11).
c++
boost-optional
c++-tr2
0x499602D2
sumber
sumber
optional
. Bayangkan Anda menginginkanoptional<int>
atau bahkan<char>
. Apakah Anda benar-benar berpikir bahwa "Zen" diperlukan untuk mengalokasikan, dereferensi, dan kemudian menghapus secara dinamis - sesuatu yang sebaliknya tidak memerlukan alokasi apa pun dan pas di tumpukan, dan mungkin bahkan dalam register?std::optional
(sementara bisa untukstd::unique_ptr
). Lebih tepatnya, standar mensyaratkan bahwa T [...] harus memenuhi persyaratan Destructible .Jawaban:
Contoh paling sederhana yang dapat saya pikirkan:
Hal yang sama mungkin dapat diselesaikan dengan argumen referensi sebagai gantinya (seperti dalam tanda tangan berikut), tetapi menggunakan
std::optional
membuat tanda tangan dan penggunaan lebih baik.Cara lain yang bisa dilakukan ini sangat buruk :
Ini membutuhkan alokasi memori yang dinamis, mengkhawatirkan kepemilikan, dll. - selalu lebih suka salah satu dari dua tanda tangan lainnya di atas.
Contoh lain:
Ini lebih disukai daripada memiliki sesuatu seperti
std::unique_ptr<std::string>
untuk setiap nomor telepon!std::optional
memberi Anda lokalitas data, yang sangat bagus untuk kinerja.Contoh lain:
Jika pencarian tidak memiliki kunci tertentu di dalamnya, maka kita dapat dengan mudah mengembalikan "tidak ada nilai."
Saya bisa menggunakannya seperti ini:
Contoh lain:
Ini jauh lebih masuk akal daripada, katakanlah, memiliki empat fungsi kelebihan yang mengambil setiap kemungkinan kombinasi
max_count
(atau tidak) danmin_match_score
(atau tidak)!Hal ini juga menghilangkan yang terkutuk "Lulus
-1
untukmax_count
jika Anda tidak ingin batas" atau "Lulusstd::numeric_limits<double>::min()
untukmin_match_score
jika Anda tidak ingin skor minimal"!Contoh lain:
Jika string kueri tidak ada
s
, saya ingin "tidakint
" - bukan nilai khusus apa pun yang diputuskan seseorang untuk digunakan untuk tujuan ini (-1?).Untuk contoh tambahan, Anda dapat melihat
boost::optional
dokumentasi .boost::optional
danstd::optional
pada dasarnya akan identik dalam hal perilaku dan penggunaan.sumber
std::optional<T>
hanya aT
dan abool
. Implementasi fungsi anggota sangat sederhana. Kinerja seharusnya tidak benar-benar menjadi perhatian ketika menggunakannya - ada kalanya sesuatu bersifat opsional, dalam hal ini sering kali merupakan alat yang tepat untuk pekerjaan itu.std::optional<T>
jauh lebih kompleks dari itu. Ini menggunakan penempatannew
dan lebih banyak hal lainnya dengan penjajaran dan ukuran yang tepat untuk membuatnya menjadi tipe literal (yaitu digunakan denganconstexpr
) di antara hal-hal lainnya. NaifT
danbool
pendekatan akan gagal dengan cepat.union storage_t { unsigned char dummy_; T value_; ... }
Baris 289:struct optional_base { bool init_; storage_t<T> storage_; ... }
Bagaimana itu bukan "aT
dan abool
"? Saya sepenuhnya setuju bahwa penerapannya sangat rumit dan nontrivial, tetapi secara konsep dan konkret tipenya adalah aT
dan abool
. "NaifT
danbool
pendekatannya akan gagal dengan cepat." Bagaimana Anda bisa membuat pernyataan ini ketika melihat kode?struct{bool,maybe_t<T>}
serikat hanya ada untuk tidak melakukanstruct{bool,T}
yang akan membangun T dalam semua kasus.std::unique_ptr<T>
danstd::optional<T>
dalam beberapa hal memenuhi peran "opsionalT
". Saya akan menggambarkan perbedaan di antara mereka sebagai "detail implementasi": alokasi tambahan, manajemen memori, lokalitas data, biaya pemindahan, dll. Saya tidak akan pernah memilikistd::unique_ptr<int> try_parse_int(std::string s);
contoh, karena itu akan menyebabkan alokasi untuk setiap panggilan meskipun tidak ada alasan untuk . Saya tidak akan pernah memiliki kelas denganstd::unique_ptr<double> limit;
- mengapa alokasi dan kehilangan lokalitas data?Contoh dikutip dari Makalah yang diadopsi baru: N3672, std :: opsional :
sumber
int
hierarki panggilan naik atau turun, alih-alih memberikan nilai "hantu" "yang dianggap" memiliki makna "kesalahan".str2int()
penerapan konversi seperti yang diinginkannya, (B) terlepas dari metode untuk memperolehnyastring s
, dan (C) menyampaikan makna penuh melaluioptional<int>
alih - alih cara angka-angka,bool
referensi, atau alokasi dinamis berdasarkan alokasi lakukanlah.Pertimbangkan ketika Anda menulis API dan Anda ingin menyatakan bahwa nilai "tidak memiliki pengembalian" bukanlah kesalahan. Misalnya, Anda perlu membaca data dari soket, dan ketika blok data selesai, Anda menguraikannya dan mengembalikannya:
Jika data yang ditambahkan menyelesaikan blok yang dapat diuraikan, Anda dapat memprosesnya; jika tidak, terus membaca dan menambahkan data:
Edit: tentang sisa pertanyaan Anda:
Ketika Anda menghitung nilai dan perlu mengembalikannya, itu membuat semantik yang lebih baik untuk kembali dengan nilai daripada mengambil referensi ke nilai output (yang mungkin tidak dihasilkan).
Bila Anda ingin memastikan bahwa kode klien memiliki untuk memeriksa nilai output (siapa pun menulis kode klien mungkin tidak memeriksa kesalahan - jika Anda mencoba untuk menggunakan pointer un-diinisialisasi Anda mendapatkan core dump, jika Anda mencoba untuk menggunakan un- menginisialisasi std :: opsional, Anda mendapatkan pengecualian tangkapan-mampu).
Sebelum ke C ++ 11, Anda harus menggunakan antarmuka yang berbeda untuk "fungsi yang mungkin tidak mengembalikan nilai" - baik kembali dengan pointer dan memeriksa NULL, atau menerima parameter output dan mengembalikan kode kesalahan / hasil untuk "tidak tersedia ".
Keduanya memaksakan upaya dan perhatian ekstra dari implementer klien untuk melakukannya dengan benar dan keduanya merupakan sumber kebingungan (yang pertama mendorong implementer klien untuk memikirkan operasi sebagai alokasi dan membutuhkan kode klien untuk menerapkan logika penanganan pointer dan yang kedua memungkinkan kode klien untuk melarikan diri dengan menggunakan nilai yang tidak valid / tidak diinisialisasi).
std::optional
dengan baik menangani masalah yang timbul dengan solusi sebelumnya.sumber
boost::
bukanstd::
?boost::optional
karena setelah sekitar dua tahun menggunakannya, itu dikodekan ke dalam korteks pre-frontal saya).Saya sering menggunakan opsional untuk mewakili data opsional yang diambil dari file konfigurasi, yaitu untuk mengatakan di mana data itu (seperti dengan elemen yang diharapkan, namun tidak perlu, dalam dokumen XML) disediakan secara opsional, sehingga saya dapat secara eksplisit dan jelas menunjukkan jika data sebenarnya ada dalam dokumen XML. Terutama ketika data dapat memiliki status "tidak disetel", versus status "kosong" dan "set" (logika fuzzy). Dengan opsional, set dan tidak set jelas, juga kosong akan jelas dengan nilai 0 atau nol.
Ini dapat menunjukkan bagaimana nilai "tidak disetel" tidak setara dengan "kosong". Dalam konsep, penunjuk ke int (int * p) dapat menunjukkan ini, di mana null (p == 0) tidak disetel, nilai 0 (* p == 0) disetel dan kosongkan, dan nilai lainnya (* p <> 0) diatur ke nilai.
Sebagai contoh praktis, saya memiliki sepotong geometri yang ditarik dari dokumen XML yang memiliki nilai yang disebut flag render, di mana geometri dapat menimpa flag render (set), menonaktifkan flag render (set ke 0), atau tidak mempengaruhi bendera render (tidak disetel), opsional akan menjadi cara yang jelas untuk mewakili ini.
Jelas pointer ke int, dalam contoh ini, dapat mencapai tujuan, atau lebih baik, share pointer karena dapat menawarkan implementasi yang lebih bersih, namun, saya berpendapat ini tentang kejelasan kode dalam kasus ini. Apakah null selalu "tidak disetel"? Dengan sebuah pointer, itu tidak jelas, karena nol secara harfiah berarti tidak dialokasikan atau dibuat, meskipun bisa , namun tidak selalu berarti "tidak disetel". Perlu menunjukkan bahwa pointer harus dilepaskan, dan dalam praktik yang baik diatur ke 0, namun, seperti dengan pointer bersama, opsional tidak memerlukan pembersihan eksplisit, jadi tidak ada masalah mencampur pembersihan dengan opsional belum diatur.
Saya percaya ini tentang kejelasan kode. Kejelasan mengurangi biaya pemeliharaan kode, dan pengembangan. Pemahaman yang jelas tentang niat kode sangat berharga.
Penggunaan pointer untuk mewakili ini akan membutuhkan konsep pointer yang berlebihan. Untuk menyatakan "null" sebagai "tidak disetel", biasanya Anda mungkin melihat satu atau lebih komentar melalui kode untuk menjelaskan maksud ini. Itu bukan solusi yang buruk dan bukan opsional, namun, saya selalu memilih untuk implementasi implisit daripada komentar eksplisit, karena komentar tidak dapat ditegakkan (seperti dengan kompilasi). Contoh item implisit ini untuk pengembangan (artikel-artikel dalam pengembangan yang disediakan murni untuk menegakkan niat) termasuk berbagai cor gaya C ++, "const" (terutama pada fungsi anggota), dan tipe "bool", untuk beberapa nama. Mungkin Anda tidak benar-benar membutuhkan fitur kode ini, asalkan semua orang mematuhi niat atau komentar.
sumber