รหัสแม่แบบนี้จะทำให้ขนาดของอาร์เรย์ทำงานอย่างไร


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....
Basile Starynkevitch

@BasileStarynkevitch ฉันยังไม่ได้อ่าน รหัสจะปรากฏในห้องสมุดบุคคลที่สาม ฉันแค่ต้องการหาความหมาย
Shadow อสูร

ดูnorvig.com/21-days.htmlเพื่อรับข้อมูลเชิงลึกที่เป็นประโยชน์ (และดูว่าใครเป็นผู้เขียนหน้านั้น)
Basile Starynkevitch

2
ดูเหมือนซ้ำกับstackoverflow.com/questions/6106158/ …
sharptooth

@BasileStarynkevitch ฉันไม่เข้าใจความเกี่ยวข้องของลิงก์นั้น
การแข่งขัน Lightness ใน Orbit

คำตอบ:


86

อันนี้จริง ๆ แล้วยากมากที่จะอธิบาย แต่ฉันจะให้ไป ...

ประการแรกdimofบอกมิติหรือจำนวนองค์ประกอบในอาร์เรย์ (ฉันเชื่อว่า "มิติ" เป็นคำศัพท์ที่ต้องการในสภาพแวดล้อมการเขียนโปรแกรม Windows)

สิ่งนี้มีความจำเป็นเพราะC++และCไม่ได้ให้วิธีแบบเนทีฟในการกำหนดขนาดของอาร์เรย์


บ่อยครั้งที่ผู้คนคิดว่าsizeof(myArray)จะใช้งานได้ แต่จริง ๆ แล้วจะทำให้คุณมีขนาดในหน่วยความจำมากกว่าจำนวนองค์ประกอบ แต่ละองค์ประกอบอาจใช้หน่วยความจำมากกว่า 1 ไบต์!

sizeof(myArray) / sizeof(myArray[0])ต่อไปพวกเขาอาจจะพยายาม นี่จะทำให้ขนาดในหน่วยความจำของอาร์เรย์หารด้วยขนาดขององค์ประกอบแรก มันใช้ได้และใช้กันอย่างแพร่หลายในCรหัส ปัญหาสำคัญของเรื่องนี้คือมันจะปรากฏขึ้นหากคุณผ่านตัวชี้แทนที่จะเป็นอาร์เรย์ ขนาดของตัวชี้ในหน่วยความจำมักจะเป็น 4 หรือ 8 ไบต์แม้ว่าสิ่งที่ชี้ไปอาจเป็นอาร์เรย์ขององค์ประกอบ 1000s


ดังนั้นสิ่งต่อไปที่ต้องลอง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 chars การแสดงออกไม่จำเป็นต้องสามารถใช้งานได้จริง แต่มันก็สมเหตุสมผลที่เวลารวบรวม

ดังนั้นsizeof(DimofSizeHelper(myArray))จะบอกขนาดในเวลารวบรวมของสิ่งที่คุณจะได้รับถ้าคุณเรียกใช้ฟังก์ชันจริง แม้ว่าเราจะไม่เรียกมันว่าจริง

Austin Powers Cross-Eyed


ไม่ต้องกังวลหากบล็อกสุดท้ายนั้นไม่สมเหตุสมผล มันเป็นกลอุบายที่แปลกประหลาดในการแก้ไขกรณีที่แปลกประหลาด นี่คือเหตุผลที่คุณไม่ได้เขียนโค้ดประเภทนี้ด้วยตัวคุณเองและให้ผู้ใช้ไลบรารี่กังวลเกี่ยวกับเรื่องไร้สาระประเภทนี้


3
@Shadowfiend มันก็ผิดเหมือนกัน สิ่งต่าง ๆ น่าเกลียดกว่านั้นเพราะมันไม่ใช่การประกาศฟังก์ชั่นมันเป็นการประกาศการอ้างอิงฟังก์ชั่น ... ฉันยังคงหาวิธีที่จะอธิบายได้
BoBTFish

5
ทำไมการประกาศฟังก์ชั่นอ้างอิง? "&" ก่อนหน้า "DimofSizeHelper" หมายถึงประเภทส่งคืนเป็นอักขระถ่าน (&) [N] ซึ่งสอดคล้องกับคำตอบของ bolov
Shadow อสูร

3
@Shadowfiend ถูกต้องอย่างแน่นอน ฉันแค่พูดถึงขยะเพราะสมองของฉันถูกมัดเป็นปม
BoBTFish

Dimension ไม่ใช่จำนวนขององค์ประกอบในอาร์เรย์ นั่นคือคุณอาจมีอาร์เรย์มิติที่ 1, 2, 3 หรือสูงกว่าซึ่งแต่ละอันมีจำนวนองค์ประกอบเท่ากัน เช่นอาร์เรย์ 1D [1000], อาร์เรย์ 2D [10] [100], array3D [10] [10] [10] แต่ละอันมีองค์ประกอบ 1,000 รายการ
jamesqf

1
@jamesqf ในภาษาอย่าง C ++ อาร์เรย์หลายมิติเป็นเพียงอาร์เรย์ที่มีอาร์เรย์อื่น จากมุมมองของคอมไพเลอร์จำนวนองค์ประกอบในอาเรย์หลักมักไม่เกี่ยวข้องกับเนื้อหาอย่างสมบูรณ์ซึ่งอาจเป็นอาเรย์รองหรือตติยภูมิ
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];

DimofSizeHelperเป็นฟังก์ชั่นเทมเพลตซึ่งรับT(&)[N]พารามิเตอร์ - การอ้างอิงถึงอาเรย์ C ขององค์ประกอบ N ชนิดTและส่งคืนchar (&)[N]อาคาอ้างอิงถึงอาเรย์ของ N ตัวอักษร ใน C ++ char มีการปลอมตัวเป็นไบต์และ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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.