Bagaimana kode templat ini untuk mendapatkan ukuran array bekerja?

61

Saya bertanya-tanya mengapa kode semacam ini bisa mendapatkan ukuran array tes? Saya tidak terbiasa dengan tata bahasa di template. Mungkin seseorang bisa menjelaskan arti kode di bawah template<typename,size_t>. Selain itu, tautan referensi juga disukai.

#define dimof(array) (sizeof(DimofSizeHelper(array)))
template <typename T, size_t N>
char(&DimofSizeHelper(T(&array)[N]))[N];

void InitDynCalls()
{
    char test[20];
    size_t n = dimof(test);
    printf("%d", n);
}
Iblis bayangan
sumber
Apakah Anda membaca sesuatu seperti n3337 tentang C ++ 11 ? Itu harus relevan dengan pertanyaan Anda! Apakah Anda mempertimbangkan untuk menggunakan std::arrayatau std::vector....
Basile Starynkevitch
@ BasileStarynkevitch Saya belum membacanya. Kode muncul di perpustakaan pihak ketiga. Saya hanya ingin mengetahui artinya.
Shadow fiend
Lihat juga norvig.com/21-days.html untuk wawasan yang bermanfaat (dan lihat siapa penulis halaman itu).
Basile Starynkevitch
2
Tampak seperti duplikat dari stackoverflow.com/questions/6106158/…
sharptooth
@ BasileStarynkevitch Saya tidak mengerti relevansi tautan itu.
Lightness Races in Orbit

Jawaban:

86

Ini sebenarnya sangat sulit untuk dijelaskan, tapi saya akan mencobanya ...

Pertama, dimofmemberi tahu Anda dimensi , atau jumlah elemen dalam array. (Saya percaya "dimensi" adalah terminologi yang lebih disukai di lingkungan pemrograman Windows).

Ini perlu karena C++dan Ctidak memberi Anda cara asli untuk menentukan ukuran array.


Seringkali orang beranggapan sizeof(myArray)akan bekerja, tetapi itu sebenarnya akan memberi Anda ukuran dalam memori, bukan jumlah elemen. Setiap elemen mungkin membutuhkan lebih dari 1 byte memori!

Selanjutnya, mereka mungkin mencoba sizeof(myArray) / sizeof(myArray[0]). Ini akan memberikan ukuran dalam memori array, dibagi dengan ukuran elemen pertama. Tidak apa-apa, dan banyak digunakan dalam Ckode. Masalah utama dengan ini adalah bahwa hal itu akan berfungsi jika Anda melewatkan pointer bukan array. Ukuran pointer di memori biasanya akan 4 atau 8 byte, meskipun hal itu menunjuk mungkin menjadi array elemen 1000s.


Jadi hal berikutnya yang harus dicoba C++adalah menggunakan templat untuk memaksa sesuatu yang hanya berfungsi untuk array, dan akan memberikan kesalahan kompilator pada pointer. Ini terlihat seperti ini:

template <typename T, std::size_t N>
std::size_t ArraySize(T (&inputArray)[N])
{
    return N;
}
//...
float x[7];
cout << ArraySize(x); // prints "7"

Template hanya akan berfungsi dengan array. Ini akan menyimpulkan jenis (tidak benar-benar diperlukan, tetapi harus ada di sana untuk mendapatkan template untuk bekerja) dan ukuran array, lalu mengembalikan ukurannya. Cara templat ini ditulis tidak dapat bekerja dengan pointer.

Biasanya Anda bisa berhenti di sini, dan ini ada di C ++ Standard Libary as std::size.


Peringatan: di bawah sini ia masuk ke wilayah pengacara bahasa berbulu.


Ini cukup keren, tetapi masih gagal dalam kasus tepi yang tidak jelas:

struct Placeholder {
    static float x[8];
};

template <typename T, int N>
int ArraySize (T (&)[N])
{
    return N;
}

int main()
{
    return ArraySize(Placeholder::x);
}

Perhatikan bahwa array xyang dideklarasikan , tetapi tidak didefinisikan . Untuk memanggil suatu fungsi (yaitu ArraySize) dengannya, xharus ditentukan .

In function `main':
SO.cpp:(.text+0x5): undefined reference to `Placeholder::x'
collect2: error: ld returned 1 exit status

Anda tidak dapat menautkan ini.


Kode yang Anda miliki dalam pertanyaan adalah cara mengatasinya. Alih-alih benar-benar memanggil fungsi, kami mendeklarasikan fungsi yang mengembalikan objek dengan ukuran yang tepat . Lalu kami menggunakan sizeoftrik itu.

Ini terlihat seperti kita memanggil fungsi, tetapi sizeofmurni kompilasi waktu konstruksi, sehingga fungsi pernah benar-benar dipanggil.

template <typename T, size_t N>
char(&DimofSizeHelper(T(&array)[N]))[N];
^^^^ ^                               ^^^
// a function that returns a reference to array of N chars - the size of this array in memory will be exactly N bytes

Catatan Anda sebenarnya tidak bisa mengembalikan array dari suatu fungsi, tetapi Anda bisa mengembalikan referensi ke array.

Kemudian DimofSizeHelper(myArray)adalah ekspresi yang tipe array pada N chars. Ekspresi sebenarnya tidak harus dapat dijalankan, tetapi masuk akal pada waktu kompilasi.

Karena itu sizeof(DimofSizeHelper(myArray))akan memberi tahu Anda ukuran pada waktu kompilasi apa yang akan Anda dapatkan jika Anda benar-benar memanggil fungsi tersebut. Meskipun sebenarnya kita tidak menyebutnya.

Austin Powers Cross-Eyed


Jangan khawatir jika blok terakhir itu tidak masuk akal. Ini adalah trik aneh untuk mengatasi kasus tepi yang aneh. Inilah sebabnya mengapa Anda tidak menulis kode semacam ini sendiri, dan biarkan pelaksana perpustakaan khawatir tentang omong kosong semacam ini.

BoBTFish
sumber
3
@Shadowfiend Ini juga salah. Hal-hal bahkan lebih buruk dari itu, karena itu sebenarnya bukan deklarasi fungsi, itu deklarasi referensi fungsi ... Saya masih mencari cara bagaimana menjelaskannya.
BoBTFish
5
Mengapa ini merupakan deklarasi referensi fungsi? Tanda "&" sebelum "DimofSizeHelper" berarti tipe yang dikembalikan adalah char (&) [N], sesuai dengan jawaban bolov.
Shadow fiend
3
@Shadowfiend Benar sekali. Saya hanya berbicara tentang sampah karena otak saya terikat.
BoBTFish
Dimensi bukan jumlah elemen dalam array. Artinya, Anda mungkin memiliki 1, 2, 3, atau array berdimensi lebih tinggi, yang masing-masing dapat memiliki jumlah elemen yang sama. Misalnya array1D [1000], array 2D [10] [100], array3D [10] [10] [10]. masing-masing memiliki 1000 elemen.
jamesqf
1
@ jamesqf Dalam bahasa seperti C ++, array multidimensi hanyalah sebuah array yang berisi array lain. Dari sudut pandang kompiler, jumlah elemen dalam array primer seringkali sama sekali tidak terkait dengan kontennya - yang mungkin merupakan array sekunder atau tersier.
Phlarx
27
template <typename T, size_t N>
char(&DimofSizeHelper(T(&array)[N]))[N];

// see it like this:
//                char(&DimofSizeHelper(T(&array)[N]))[N];
// template name:       DimofSizeHelper
// param name:                             array
// param type:                          T(&     )[N])
// return type:   char(&                             )[N];

DimofSizeHelperadalah fungsi templat yang mengambil T(&)[N]parameter - alias referensi ke C-array elemen N jenis Tdan mengembalikan char (&)[N]alias referensi ke array N karakter. Dalam C ++ char adalah byte dalam penyamaran dan sizeof(char)dijamin 1oleh standar.

size_t n = dimof(test);
// macro expansion:
size_t n = sizeof(DimofSizeHelper(array));

nditugaskan ukuran tipe pengembalian DimofSizeHelper, sizeof(char[N])yang merupakan N.


Ini agak berbelit-belit dan tidak perlu. Cara yang biasa dilakukan adalah:

template <class T, size_t N>
/*constexpr*/ size_t sizeof_array(T (&)[N]) { return N; }

Karena C ++ 17 ini juga tidak perlu, seperti yang kita std::sizelakukan ini, tetapi dengan cara yang lebih umum, bisa mendapatkan ukuran wadah gaya stl.


Seperti yang ditunjukkan oleh BoBTFish, perlu untuk kasus tepi.

bolov
sumber
2
Ini perlu jika Anda tidak dapat menggunakan ODR array yang ingin Anda ambil ukurannya (dinyatakan tetapi tidak didefinisikan). Harus diakui, sangat tidak jelas.
BoBTFish
Terima kasih telah menjelaskan jenis dalam fungsi templat. Itu sangat membantu.
Shadow fiend
3
Kami telah std::extentsejak C ++ 11 yang merupakan waktu kompilasi.
LF