Як працює цей шаблон коду, щоб отримати розмір масиву?


61

Цікаво, чому такий код може отримати розмір тестового масиву? Я не знайома з граматикою в шаблоні. Можливо, хтось міг би пояснити значення коду під template<typename,size_t>. Крім того, посилання на посилання також є кращим.

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


Ви читали щось на кшталт n3337 про C ++ 11 ? Це має відповідати вашому питанню! Ви розглядали можливість використання std::arrayабо std::vector....
Базиль Старинкевич

@BasileStarynkevitch Я цього не читав. Код з’являється в сторонній бібліотеці. Я просто хочу з’ясувати значення.
Shadow

Дивіться також norvig.com/21-days.html для корисного розуміння (і подивіться, хто є автором цієї сторінки).
Василь Старинкевич

2
Виглядає як дублікат stackoverflow.com/questions/6106158 / ...
Sharptooth

@BasileStarynkevitch Я не розумію актуальності цього посилання.
Гонки легкості по орбіті

Відповіді:


86

Це насправді дуже важко пояснити, але я підкажу ...

По-перше, dimofповідомляє вам розмірність або кількість елементів у масиві. (Я вважаю, що "розмірність" є кращою термінологією в програмуючих середовищах Windows).

Це необхідно, тому що C++і Cне дають вам рідного способу визначення розміру масиву.


Часто люди припускають, що sizeof(myArray)вони спрацюють, але це фактично надасть вам розмір пам’яті, а не кількість елементів. Напевно, кожен елемент займає більше 1 байта пам'яті!

Далі вони можуть спробувати sizeof(myArray) / sizeof(myArray[0]). Це дало б розмір в пам'яті масиву, поділене на розмір першого елемента. Це нормально і широко використовується в Cкоді. Основна проблема з цим полягає в тому, що воно буде працювати, якщо ви передасте вказівник замість масиву. Розмір вказівника в пам'яті зазвичай становитиме 4 або 8 байт, навіть якщо річ, на яку він вказує, може бути масивом з 1000-ти елементів.


Отже, наступне, що потрібно спробувати, C++- це використовувати шаблони, щоб змусити щось, що працює лише для масивів, і дасть помилку компілятора на покажчик. Це виглядає приблизно так:

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

Шаблон працюватиме лише з масивом. Він виведе тип (не дуже потрібен, але повинен бути там, щоб шаблон запрацював) та розмір масиву, після чого він поверне розмір. Спосіб написання шаблону не може працювати з покажчиком.

Зазвичай ви можете зупинитися тут, і це в Стандартній бібліотеці C ++ як std::size.


Попередження: внизу тут він потрапляє на волохату мову-адвокатську територію.


Це дуже круто, але все ж не вдається в незрозумілому краєвому випадку:

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

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

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

Зверніть увагу , що масив xбуде оголошений , але не визначено . Щоб викликати функцію (тобто ArraySize) з нею, xнеобхідно визначити .

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

Ви не можете зв’язати це.


Код, який ви маєте у запитанні, є способом цього подолати. Замість того, щоб насправді викликати функцію, ми оголошуємо функцію, яка повертає об’єкт точно потрібного розміру . Тоді ми використовуємо sizeofфокус на цьому.

Це виглядає , як ми називаємо функцію, але sizeofце чисто час компіляції конструкт, тому функція фактично ніколи не викликається.

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

Зауважте, ви фактично не можете повернути масив з функції, але ви можете повернути посилання на масив.

Тоді DimofSizeHelper(myArray)це вираз , чий тип є масивом на N charс. Вираз насправді не повинен бути досяжним, але це має сенс під час компіляції.

Тому sizeof(DimofSizeHelper(myArray))підкаже вам розмір за час компіляції того, що ви отримали, якби ви насправді викликали функцію. Незважаючи на те, що ми насправді не називаємо це.

Остін Пауерс схрещеними очима


Не хвилюйтесь, якщо цей останній блок не мав сенсу. Це химерний трюк, щоб обходити справу з химерним краєм. Ось чому ви не пишете такий код самостійно, а дозвольте виконавцям бібліотек хвилюватися з приводу такої дурниці.


3
@Shadowfiend Це теж неправильно. Речі ще гірші, ніж це, тому що насправді це не декларування функції, це оголошення посилання на функцію ... Я все ще з'ясовую, як це можливо пояснити.
BoBTFish

5
Чому це оголошення посилання на функцію? Значення "&" перед "DimofSizeHelper" означає тип повернення char (&) [N], який відповідає відповіді болова.
Shadow

3
@Shadowfiend Абсолютно правильно. Я просто говорив сміття, бо мій мозок зав'язаний у вузол.
BoBTFish

Dimension - це не кількість елементів масиву. Тобто, у вас можуть бути 1, 2, 3 або вищі розмірні масиви, які можуть мати однакову кількість елементів. Напр. Масив1D [1000], масив 2D [10] [100], масив3D [10] [10] [10]. кожен з яких має 1000 елементів.
jamesqf

1
@jamesqf У таких мовах, як C ++, багатовимірний масив - це просто масив, який містить інші масиви. З точки зору компілятора, кількість елементів у первинному масиві часто абсолютно не пов’язана з його вмістом - який може бути вторинним або третинним масивами.
Фларкс

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];

DimofSizeHelper- це шаблонна функція, яка приймає T(&)[N]параметр - він також посилання на масив С з N елементів типу Tі повертає char (&)[N]ака посилання на масив з N символів. У мові C ++ значок є переодягненим байтом і sizeof(char)гарантовано 1відповідає стандарту.

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

nприсвоюється розмір типу повернення DimofSizeHelper, sizeof(char[N])який є N.


Це трохи заплутано і непотрібне. Звичайний спосіб зробити це:

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

Оскільки C ++ 17, це теж непотрібно, як це ми маємо, std::sizeщо це робить, але більш загальним чином, можливість отримати розмір будь-якого контейнера в стилі stl.


Як вказував BoBTFish, це необхідно для кращого випадку.


2
Це необхідно, якщо ви не можете використовувати ODR-масив, для якого потрібно прийняти розмір (він оголошений, але не визначений). Справді, досить незрозуміло.
BoBTFish

Дякуємо, що пояснили тип у функції шаблону. Це справді допомагає.
Shadow

3
std::extentЗ C ++ 11 ми маємо час компіляції.
LF
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.