Saya mencoba mengakses konten varian. Saya tidak tahu apa yang ada di sana, tapi untungnya varian tidak. Jadi saya pikir saya akan bertanya pada varian apa indeksnya dan kemudian gunakan indeks itu untuk std::get
isinya.
Tetapi ini tidak mengkompilasi:
#include <variant>
int main()
{
std::variant<int, float, char> var { 42.0F };
const std::size_t idx = var.index();
auto res = std::get<idx>(var);
return 0;
}
Kesalahan terjadi dalam std::get
panggilan:
error: no matching function for call to ‘get<idx>(std::variant<int, float, char>&)’
auto res = std::get<idx>(var);
^
In file included from /usr/include/c++/8/variant:37,
from main.cpp:1:
/usr/include/c++/8/utility:216:5: note: candidate: ‘template<long unsigned int _Int, class _Tp1, class _Tp2> constexpr typename std::tuple_element<_Int, std::pair<_Tp1, _Tp2> >::type& std::get(std::pair<_Tp1, _Tp2>&)’
get(std::pair<_Tp1, _Tp2>& __in) noexcept
^~~
/usr/include/c++/8/utility:216:5: note: template argument deduction/substitution failed:
main.cpp:9:31: error: the value of ‘idx’ is not usable in a constant expression
auto res = std::get<idx>(var);
^
main.cpp:7:15: note: ‘std::size_t idx’ is not const
std::size_t idx = var.index();
^~~
Bagaimana saya bisa memperbaikinya?
Jawaban:
Pada dasarnya, Anda tidak bisa.
Kau menulis:
... tetapi hanya pada saat dijalankan, bukan pada waktu kompilasi.
Dan itu berarti
idx
nilai Anda bukan waktu kompilasi.Dan itu berarti Anda tidak dapat menggunakan
get<idx>()
secara langsung.Sesuatu yang bisa Anda lakukan adalah memiliki pernyataan switch; jelek, tapi itu akan berhasil:
Namun ini agak jelek. Seperti komentar menyarankan, Anda mungkin juga
std::visit()
(yang tidak jauh berbeda dari kode di atas, kecuali menggunakan argumen templat variadic bukannya eksplisit ini) dan menghindari beralih sama sekali. Untuk pendekatan berbasis indeks lainnya (tidak khusus untukstd::variant
), lihat:Idiom untuk mensimulasikan parameter templat angka waktu-berjalan?
sumber
Kompilator perlu mengetahui nilai
idx
pada waktu kompilasi untukstd::get<idx>()
bekerja, karena sedang digunakan sebagai argumen templat.Opsi pertama: Jika kode dimaksudkan untuk dijalankan pada waktu kompilasi, maka buat semuanya
constexpr
:Ini bekerja karena
std::variant
adalahconstexpr
ramah (konstruktor dan metode semuaconstexpr
).Opsi kedua: Jika kode tidak dimaksudkan untuk berjalan pada waktu kompilasi, yang kemungkinan terjadi, kompiler tidak dapat menyimpulkan pada waktu kompilasi jenis
res
, karena itu bisa tiga hal yang berbeda (int
,float
atauchar
). C ++ adalah bahasa yang diketik secara statis, dan kompilator harus dapat menyimpulkan tipe dariauto res = ...
ekspresi yang mengikuti (artinya harus selalu tipe yang sama).Anda dapat menggunakan
std::get<T>
, dengan jenis alih-alih indeks, jika Anda sudah tahu apa itu:Secara umum, gunakan
std::holds_alternative
untuk memeriksa apakah varian memegang masing-masing jenis yang diberikan, dan menanganinya secara terpisah:Atau Anda bisa menggunakan
std::visit
. Ini sedikit lebih rumit: Anda dapat menggunakan fungsi lambda / templated yang bertipe agnostik dan berfungsi untuk semua jenis varian, atau melewatkan functor dengan operator panggilan yang kelebihan beban:Lihat std :: kunjungi untuk detail dan contoh.
sumber
Masalahnya adalah yang
std::get<idx>(var);
membutuhkan (untukidx
) nilai waktu kompilasi diketahui.Jadi suatu
constexpr
nilaiTetapi untuk menginisialisasi
idx
sebagaiconstexpr
, jugavar
harusconstexpr
sumber
Masalah muncul dari templat yang sedang dipakai pada waktu kompilasi sementara indeks yang Anda dapatkan dihitung pada waktu berjalan. Demikian pula, tipe C ++ juga didefinisikan pada waktu kompilasi sehingga bahkan dengan
auto
deklarasi,res
harus memiliki tipe konkret agar program dapat terbentuk dengan baik. Ini berarti bahwa bahkan tanpa batasan pada templat, apa yang Anda coba lakukan pada dasarnya tidak mungkin untuk ekspresi non-konstanstd::variant
s. Bagaimana orang mengatasi ini?Pertama, jika varian Anda adalah ekspresi konstan, kode tersebut mengkompilasi dan berfungsi seperti yang diharapkan
Kalau tidak, Anda harus menggunakan beberapa mekanisme percabangan manual
Anda bisa mendefinisikan cabang-cabang ini menggunakan pola pengunjung, lihat std :: visit .
sumber
Ini secara inheren tidak mungkin dalam model C ++; mempertimbangkan
Yang
f
dipanggil,f<int>
atauf<double>
? Jika "keduanya", itu berarti yangg
berisi cabang (yang tidak), atau ada dua versig
(yang hanya mendorong masalah ke peneleponnya). Dan pikirkanf(T,U,V,W)
— di mana kompiler berhenti?Sebenarnya ada proposal untuk JIT untuk C ++ yang akan memungkinkan hal-hal seperti ini dengan mengkompilasi versi tambahan
f
ketika mereka dipanggil, tetapi masih sangat dini.sumber