Bir dizinin boyutunu almak için bu şablon kodu nasıl çalışır?


61

Neden bu tür bir kod test dizisinin boyutunu alabilir merak ediyorum? Şablondaki dil bilgisine aşina değilim. Belki birisi altındaki kodun anlamını açıklayabilir template<typename,size_t>. Ayrıca, bir referans linki de tercih edilmektedir.

#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);
}


C ++ 11 hakkında n3337 gibi bir şey okudunuz mu? Sorunuzla alakalı olmalı! Kullandığınız düşündünüz mü ya ....std::arraystd::vector
Basile Starynkevitch

@BasileStarynkevitch Bunu okumadım. Kod üçüncü taraf bir kitaplıkta görünür. Sadece anlamını anlamak istiyorum.
Shadow fiend

Ayrıca yararlı bir fikir edinmek için norvig.com/21-days.html adresine bakın (ve o sayfanın yazarı kim olduğuna bakın).
Basile Starynkevitch


@BasileStarynkevitch Bu bağlantının alaka düzeyini anlamıyorum.
Yörüngedeki Hafiflik Yarışları

Yanıtlar:


86

Bu aslında açıklanması gerçekten zor bir şey ama ben bir deneyeceğim ...

İlk olarak, bir dizideki öğelerin boyutunu veya öğelerini dimofsöyler . ("Boyut" un Windows programlama ortamlarında tercih edilen terminoloji olduğuna inanıyorum).

Bu gereklidir, çünkü C++ve Csize bir dizinin boyutunu belirlemek için bir yerli yol vermeyin.


Çoğu zaman insanlar sizeof(myArray)işe yarayacağını varsayarlar , ancak bu aslında öğelerin sayısından ziyade hafızadaki boyutu verecektir. Her öğe muhtemelen 1 bayttan fazla bellek alır!

Sonra deneyebilirler sizeof(myArray) / sizeof(myArray[0]). Bu, dizinin belleğindeki boyutu, ilk öğenin boyutuna böler. Tamam ve Ckodda yaygın olarak kullanılır . Bununla ilgili en büyük sorun, bir dizi yerine bir işaretçi iletirseniz işe yarayacağıdır. İşaret ettiği şey 1000'er elementlik bir dizi olsa da, bellekteki bir işaretçinin boyutu genellikle 4 veya 8 bayt olacaktır.


Denemek için bir sonraki şey C++, sadece diziler için çalışan ve bir işaretçi üzerinde bir derleyici hatası verecek bir şeyi zorlamak için şablonlar kullanmaktır. Şöyle görünüyor:

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

Şablon yalnızca bir dizi ile çalışır. Tür (gerçekten gerekli değildir, ancak şablonun çalışması için orada olması gerekir) ve dizinin boyutunu çıkarır, sonra boyutu döndürür. Şablonun yazılma şekli bir işaretçi ile çalışamaz.

Genellikle burada durabilir ve bu C ++ Standart Libary'de olduğu gibi std::size.


Uyarı: burada aşağıda kıllı dil-avukat topraklarına giriyor.


Bu oldukça havalı, ancak hala belirsiz bir kenar durumunda başarısız oluyor:

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

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

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

Dizi unutmayın xedilir ilan , ancak tanımlanmış . Bir işlevi (yani ArraySize) onunla çağırmak xiçin tanımlanmalıdır .

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

Bunu bağlayamazsınız.


Soruda bulunan kod bunun bir yoludur. Bir işlevi gerçekten çağırmak yerine, tam olarak doğru boyutta bir nesne döndüren bir işlev beyan ederiz . Sonra bu konuda sizeofhileyi kullanıyoruz .

O görünüyor biz işlevi çağırmak gibi ama sizeofişlevi gerçekte hiçbir zaman çağrılır, böylece tamamen bir derleme zamanı yapıdır.

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

İşlevden bir dizi döndüremeyeceğinizi, ancak bir diziye başvuru döndürebileceğinizi unutmayın.

Sonra türü s'de bir dizi olan DimofSizeHelper(myArray)bir ifadedir . İfadenin aslında çalıştırılabilir olması gerekmez, ancak derleme zamanında mantıklıdır.N char

Bu nedenle , işlevi gerçekten çağırırsanız ne elde edeceğinizin derleme zamanında boyutunusizeof(DimofSizeHelper(myArray)) söyleyecektir . Aslında aramamıza rağmen.

Austin Powers Şaşı


Bu son blokun bir anlamı yoksa endişelenmeyin. Tuhaf bir kenar kasasının etrafında çalışmak garip bir numara. Bu yüzden bu tür bir kodu kendiniz yazmazsınız ve kütüphane uygulayıcılarının bu tür saçmalıklar hakkında endişelenmesine izin vermezsiniz.


3
@Shadowfiend Bu da yanlış. İşler bundan daha çirkin, çünkü aslında bir fonksiyonun beyanı değil, bir fonksiyon referansının beyanı ... Hala bunu nasıl açıklayacağımı anlıyorum.
BoBTFish

5
Neden bir işlev başvurusunun ilanı? "&" Öncesi "DimofSizeHelper", dönüş türünün bolov'un cevabına göre char (&) [N] olduğu anlamına gelir.
Shadow fiend

3
@Shadowfiend Kesinlikle doğru. Sadece saçma sapan konuşuyordum çünkü beynimi bir düğüme bağladım.
BoBTFish

Boyut, bir dizideki öğelerin sayısı değildir. Yani, her biri aynı sayıda öğeye sahip olabilecek 1, 2, 3 veya daha yüksek boyutlu dizilere sahip olabilirsiniz. Örneğin dizi1D [1000], dizi 2D [10] [100], dizi3D [10] [10] [10]. her biri 1000 elemente sahiptir.
jamesqf

1
@jamesqf C ++ gibi dillerde, çok boyutlu bir dizi yalnızca diğer dizileri içeren bir dizidir. Derleyicinin bakış açısından, birincil dizideki öğelerin sayısı genellikle içerikleriyle tamamen ilişkisizdir - ikincil veya üçüncül diziler olabilir.
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];

DimofSizeHelperbir T(&)[N]parametre alan bir şablon fonksiyonudur - diğer bir deyişle, N tipindeki bir C-dizisine yapılan bir referanstır Tve bir char (&)[N]aka N karakter dizisine bir referans döndürür . C ++ 'da bir karakter gizlenmiş bayttır ve standart tarafından sizeof(char)garanti edilir 1.

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

ndönüş türü büyüklüğü atanır DimofSizeHelperolduğu, sizeof(char[N])olan N.


Bu biraz kıvrık ve gereksiz. Bunu yapmanın olağan yolu şuydu:

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

C ++ 17'den beri, bunu yaptığımız gibi, gereksizdir, std::sizeancak daha genel bir şekilde, herhangi bir stl tarzı kabın boyutunu elde edebilmek.


BoBTFish tarafından işaret edildiği gibi, bir kenar kasa için gereklidir.


2
Boyutu almak istediğiniz diziyi ODR-kullanamazsanız gereklidir (bildirilir ancak tanımlanmaz). Kuşkusuz, oldukça belirsiz.
BoBTFish

Şablon işlevindeki türü açıkladığınız için teşekkür ederiz. Bu gerçekten yardımcı oluyor.
Shadow fiend

3
Biz std::extentderleme zamanı olan C ++ 11 var.
LF
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.