Saya memiliki fungsi yang mengambil multidimensi std::vector
dan membutuhkan kedalaman (atau jumlah dimensi) untuk dilewatkan sebagai parameter templat. Alih-alih hardcoding nilai ini saya ingin menulis constexpr
fungsi yang akan mengambil std::vector
dan mengembalikan kedalaman sebagai unsigned integer
nilai.
Sebagai contoh:
std::vector<std::vector<std::vector<int>>> v =
{
{ { 0, 1}, { 2, 3 } },
{ { 4, 5}, { 6, 7 } },
};
// Returns 3
size_t depth = GetDepth(v);
Ini perlu dilakukan pada waktu kompilasi karena kedalaman ini akan diteruskan ke fungsi templat sebagai parameter templat:
// Same as calling foo<3>(v);
foo<GetDepth(v)>(v);
Apakah ada cara untuk melakukan ini?
std::vector
adalah run-time thing, bukan compile-time. Jika Anda menginginkan wadah ukuran waktu kompilasi, lihatstd::array
. Juga; ingat bahwaconstexpr
hanya berarti " dapat dievaluasi pada waktu kompilasi" - tidak ada janji bahwa itu akan terjadi. Ini dapat dievaluasi pada saat run-time.std::vector
yang bersarang di dalam satu sama lain. Misalnya denganstd::vector<std::vector<int>> v;
,GetDepth(v);
akan mengembalikan 2 karena ini adalah vektor 2 dimensi. Ukurannya tidak relevan.vector
tidak selalu merupakan cara terbaik untuk melakukan sesuatu. Pengindeksan 2d atau 3d manual dari satu vektor datar dapat lebih efisien, tergantung pada kasus penggunaan. (Hanya bilangan bulat matematika bukannya mengejar-pointer dari tingkat luar.)rank
untuk permintaan ini pada tipe array (sesuai dengan nomenklatur matematika untuk tensor). Mungkin itu kata yang lebih baik di sini daripada "kedalaman".Jawaban:
Masalah templating klasik. Berikut adalah solusi sederhana seperti bagaimana perpustakaan standar C ++. Ide dasarnya adalah untuk memiliki template rekursif yang akan menghitung satu per satu setiap dimensi, dengan kasus dasar 0 untuk semua jenis yang bukan vektor.
Jadi Anda bisa menggunakannya seperti ini:
Edit:
Ok, saya sudah menyelesaikan implementasi umum untuk semua jenis kontainer. Perhatikan bahwa saya mendefinisikan tipe kontainer sebagai segala sesuatu yang memiliki tipe iterator yang terbentuk dengan baik sesuai dengan ekspresi di
begin(t)
manastd::begin
diimpor untuk pencarian ADL dant
merupakan nilai jenisT
.Berikut kode saya bersama dengan komentar untuk menjelaskan mengapa hal-hal berfungsi dan kasus uji yang saya gunakan. Catatan, ini membutuhkan C ++ 17 untuk dikompilasi.
sumber
std::vector<T>
spesialisasi dan mengubahnya ke beberapa jenis wadah lainnya. Satu-satunya yang Anda butuhkan adalah kasus dasar 0 untuk semua jenis yang tidak Anda khususkanDengan asumsi bahwa suatu wadah adalah segala jenis yang memiliki
value_type
daniterator
jenis anggota (wadah perpustakaan standar memenuhi persyaratan ini) atau larik gaya-C, kita dapat dengan mudah menggeneralisasi solusi Cruz Jean :Jenis wadah selanjutnya dapat dibatasi jika diperlukan.
sumber
Anda dapat menentukan templat kelas berikut
vector_depth<>
yang cocok dengan jenis apa pun:Template primer ini terkait dengan case dasar yang mengakhiri rekursi. Kemudian, tentukan spesialisasi yang terkait untuk
std::vector<T>
:Spesialisasi ini cocok dengan
std::vector<T>
dan sesuai dengan kasus rekursif.Terakhir, tentukan templat fungsi
GetDepth()
,, yang menggunakan templat kelas di atas:Contoh:
Output dari program ini adalah:
sumber
std::vector
, tapi misalnyaGetDepth(v)
di manav
iniint
tidak akan dikompilasi. Akan lebih baik untuk memilikiGetDepth(const volatile T&)
dan kembalivector_depth<T>::value
.volatile
biarkan saja itu mencakup lebih banyak barang, menjadi cv maksimum yang berkualitas