Làm thế nào để mã mẫu này để có được kích thước của một mảng hoạt động?


61

Tôi tự hỏi tại sao loại mã này có thể có được kích thước của mảng thử nghiệm? Tôi không quen thuộc với ngữ pháp trong mẫu. Có lẽ ai đó có thể giải thích ý nghĩa của mã theo template<typename,size_t>. Bên cạnh đó, một liên kết tham khảo được ưa thích quá.

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


Bạn đã đọc một cái gì đó như n3337 về C ++ 11 ? Nó nên có liên quan đến câu hỏi của bạn! Bạn đã cân nhắc sử dụng std::arrayhay std::vector....
Basile Starynkevitch

@BasileStarynkevitch Tôi chưa đọc nó. Mã này xuất hiện trong thư viện của bên thứ ba. Tôi chỉ muốn tìm ra ý nghĩa.
Kẻ thù bóng tối

Xem thêm norvig.com/21-days.html để có cái nhìn sâu sắc hữu ích (và xem ai là tác giả của trang đó).
Basile Starynkevitch

2
Trông giống như một bản sao của stackoverflow.com/questions/6106158/ từ
sharptooth

@BasileStarynkevitch Tôi không hiểu sự liên quan của liên kết đó.
Các cuộc đua nhẹ nhàng trong quỹ đạo

Câu trả lời:


86

Đây thực sự là một vấn đề rất khó giải thích, nhưng tôi sẽ thử ...

Đầu tiên, dimofcho bạn biết kích thước hoặc số phần tử trong một mảng. (Tôi tin rằng "thứ nguyên" là thuật ngữ ưa thích trong môi trường lập trình Windows).

Điều này là cần thiết bởi vì C++Ckhông cung cấp cho bạn một cách riêng để xác định kích thước của một mảng.


Thông thường mọi người cho rằng sizeof(myArray)sẽ hoạt động, nhưng điều đó thực sự sẽ cung cấp cho bạn kích thước trong bộ nhớ, thay vì số lượng phần tử. Mỗi phần tử có thể mất hơn 1 byte bộ nhớ!

Tiếp theo, họ có thể thử sizeof(myArray) / sizeof(myArray[0]). Điều này sẽ cho kích thước trong bộ nhớ của mảng, chia cho kích thước của phần tử đầu tiên. Nó ổn, và được sử dụng rộng rãi trong Cmã. Vấn đề chính với điều này là nó sẽ xuất hiện để hoạt động nếu bạn vượt qua một con trỏ thay vì một mảng. Kích thước của một con trỏ trong bộ nhớ thường sẽ là 4 hoặc 8 byte, mặc dù thứ mà nó trỏ đến có thể là một mảng gồm 1000 phần tử.


Vì vậy, điều tiếp theo cần thử C++là sử dụng các mẫu để buộc một cái gì đó chỉ hoạt động cho các mảng và sẽ gây ra lỗi trình biên dịch trên một con trỏ. Nó trông như thế này:

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

Mẫu sẽ chỉ làm việc với một mảng. Nó sẽ suy ra kiểu (không thực sự cần thiết, nhưng phải ở đó để làm cho mẫu hoạt động) và kích thước của mảng, sau đó nó trả về kích thước. Cách viết mẫu không thể làm việc với một con trỏ.

Thông thường bạn có thể dừng ở đây, và đây là trong Libary Standard C ++ như std::size.


Cảnh báo: bên dưới đây, nó được đưa vào lãnh thổ luật sư ngôn ngữ đầy lông.


Điều này là khá mát mẻ, nhưng vẫn thất bại trong một trường hợp cạnh tối nghĩa:

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

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

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

Lưu ý rằng mảng xđược khai báo , nhưng không được xác định . Để gọi một hàm (nghĩa là ArraySize) với nó, xphải được xác định .

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

Bạn không thể liên kết này.


Mã bạn có trong câu hỏi là một cách xung quanh đó. Thay vì thực sự gọi một hàm, chúng ta khai báo một hàm trả về một đối tượng có kích thước chính xác . Sau đó, chúng tôi sử dụng các sizeofmẹo trên đó.

vẻ như chúng ta gọi hàm, nhưng sizeofhoàn toàn là cấu trúc thời gian biên dịch, vì vậy hàm thực sự không bao giờ được gọi.

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

Lưu ý rằng bạn thực sự không thể trả về một mảng từ một hàm, nhưng bạn có thể trả về một tham chiếu đến một mảng.

Sau đó DimofSizeHelper(myArray)là một biểu thứckiểu là một mảng trên N chars. Biểu thức không thực sự phải chạy được, nhưng nó có ý nghĩa tại thời gian biên dịch.

Do đó sizeof(DimofSizeHelper(myArray))sẽ cho bạn biết kích thước tại thời gian biên dịch của những gì bạn sẽ nhận được nếu bạn thực sự gọi hàm. Mặc dù chúng tôi không thực sự gọi nó.

Austin Powers lác mắt


Đừng lo lắng nếu khối cuối cùng không có ý nghĩa gì. Đó là một mẹo kỳ quái để làm việc xung quanh một trường hợp cạnh kỳ quái. Đây là lý do tại sao bạn không tự viết loại mã này và để những người triển khai thư viện lo lắng về loại vô nghĩa này.


3
@Shadowfiend Nó cũng sai. Mọi thứ thậm chí còn tồi tệ hơn thế, bởi vì nó không thực sự là một tuyên bố của một chức năng, đó là một tuyên bố về một tham chiếu chức năng ... Tôi vẫn đang tìm cách giải thích điều đó.
BoBTFish

5
Tại sao nó là một tuyên bố của một tham chiếu chức năng? "&" Trước "DimofSizeHelper" có nghĩa là loại trả về là char (&) [N], phù hợp với câu trả lời của bolov.
Kẻ thù bóng tối

3
@Shadowfiend Hoàn toàn đúng. Tôi chỉ đang nói chuyện rác rưởi vì tôi bị trói buộc trong não.
BoBTFish

Kích thước không phải là số lượng phần tử trong một mảng. Nghĩa là, bạn có thể có các mảng có kích thước 1, 2, 3 hoặc cao hơn, mỗi mảng có thể có cùng số phần tử. Ví dụ: mảng1D [1000], mảng 2D [10] [100], mảng3D [10] [10] [10]. mỗi cái có 1000 phần tử.
jamesqf

1
@jamesqf Trong các ngôn ngữ như C ++, một mảng nhiều chiều chỉ đơn giản là một mảng chứa các mảng khác. Từ quan điểm của nhà soạn nhạc, số lượng phần tử trong mảng chính thường hoàn toàn không liên quan đến nội dung của nó - có thể là mảng thứ cấp hoặc thứ ba.
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];

DimofSizeHelperlà một hàm mẫu có một T(&)[N]tham số - còn gọi là tham chiếu đến một mảng C gồm N phần tử loại Tvà trả về một char (&)[N]aka tham chiếu đến một mảng N ký tự. Trong C ++, char là byte được ngụy trang và sizeof(char)được đảm bảo 1theo tiêu chuẩn.

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

nđược gán kích thước của kiểu trả về DimofSizeHelper, sizeof(char[N])đó là N.


Đây là một chút phức tạp và không cần thiết. Cách thông thường để làm điều đó là:

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

Vì C ++ 17, điều này cũng không cần thiết, vì chúng ta có std::sizecái này, nhưng theo cách chung hơn, có thể có được kích thước của bất kỳ container kiểu stl nào.


Như BoBTFish đã chỉ ra, nó là cần thiết cho một trường hợp cạnh.


2
Điều này là cần thiết nếu bạn không thể sử dụng ODR mảng mà bạn muốn lấy kích thước của nó (nó được khai báo nhưng không được xác định). Phải thừa nhận, khá mơ hồ.
BoBTFish

Cảm ơn bạn đã giải thích loại trong chức năng mẫu. Điều đó thực sự có ích.
Kẻ thù bóng tối

3
Chúng tôi có std::extenttừ C ++ 11 là thời gian biên dịch.
LF
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.