簡體   English   中英

不使用宏編譯時間sizeof_array

[英]Compile time sizeof_array without using a macro

這只是過去幾天困擾我的事情,我認為不可能解決,但我之前看過模板魔術。

開始:

要獲得標准C ++數組中的元素數量,我可以使用宏(1)或類型安全內聯函數(2):

(1)

#define sizeof_array(ARRAY) (sizeof(ARRAY)/sizeof(ARRAY[0]))

(2)

template <typename T>
size_t sizeof_array(const T& ARRAY){
    return (sizeof(ARRAY)/sizeof(ARRAY[0]));
}

正如您所看到的,第一個問題是成為一個宏(目前我認為是一個問題),而另一個問題是在編譯時無法獲得數組的大小; 即我不能寫:

enum ENUM{N=sizeof_array(ARRAY)};

要么

BOOST_STATIC_ASSERT(sizeof_array(ARRAY)==10);// Assuming the size 10..

有誰知道這是否可以解決?

更新

這個問題是在引入constexpr之前創建的。 現在你可以簡單地使用:

template <typename T>
constexpr auto sizeof_array(const T& iarray) {
    return (sizeof(iarray) / sizeof(iarray[0]));
}

這里嘗試以下內容:

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

int testarray[10];
enum { testsize = mycountof(testarray) };

void test() {
    printf("The array count is: %d\n", testsize);
}

它應該打印出來:“數組計數是:10”

在C ++中,1x constexpr將為您提供:

template <typename T, size_t N>
constexpr size_t countof(T(&)[N])
{
    return N;
}

我能想到的最好的是:

template <class T, std::size_t N>
char (&sizeof_array(T (&a)[N]))[N];

// As litb noted in comments, you need this overload to handle array rvalues
// correctly (e.g. when array is a member of a struct returned from function),
// since they won't bind to non-const reference in the overload above.
template <class T, std::size_t N>
char (&sizeof_array(const T (&a)[N]))[N];

必須與另一個sizeof一起使用:

int main()
{
    int a[10];
    int n = sizeof(sizeof_array(a));
    std::cout << n << std::endl;
}

[編輯]

想想看,我相信除了宏之外,在C ++ 03中單獨的“類似函數的調用”這是不可能做到的,這就是原因。

一方面,您顯然需要模板參數推導來獲得數組的大小(直接或通過sizeof )。 但是,模板參數推導僅適用於函數,而不適用於類; 即你可以有一個類型為reference-to-array-of-N的模板參數R,其中N是另一個模板參數,但是你必須在調用點提供R和N. 如果你想從R中推導出N,那么只有函數調用才能做到。

另一方面,任何涉及函數調用的表達式都可以是常量的唯一方法是它在sizeof內部。 其他任何東西(例如,在函數的返回值上訪問靜態或枚舉成員)仍然需要進行函數調用,這顯然意味着這不是常量表達式。

問題

我喜歡Adisak的回答

template <typename T, size_t N>
char ( &_ArraySizeHelper( T (&arr)[N] ))[N];
#define COUNTOF( arr ) (sizeof( _ArraySizeHelper( arr ) ))

這是微軟在VS2008中用於_countof宏的東西,它有一些不錯的功能:

  • 它在編譯時運行
  • 它是類型安全的 (即如果你給它一個指針,它將產生編譯時錯誤,這些數組也很容易降級)

但正如Georg指出的那樣,這種方法使用模板,所以不保證可以使用C ++ 03的本地類型

void i_am_a_banana() {
  struct { int i; } arr[10];
  std::cout << COUNTOF(arr) << std::endl; // forbidden in C++03
}

幸運的是,我們沒有運氣。

解決方案

Ivan Johnson想出了一個聰明的方法 ,贏得所有帳戶:它是類型安全的,編譯時,並使用本地類型:

#define COUNTOF(arr) ( \
   0 * sizeof(reinterpret_cast<const ::Bad_arg_to_COUNTOF*>(arr)) + \
   0 * sizeof(::Bad_arg_to_COUNTOF::check_type((arr), &(arr))) + \
   sizeof(arr) / sizeof((arr)[0]) )

struct Bad_arg_to_COUNTOF {
   class Is_pointer; // incomplete
   class Is_array {};
   template <typename T>
   static Is_pointer check_type(const T*, const T* const*);
   static Is_array check_type(const void*, const void*);
};

對於那些感興趣的人,它通過在標准的基於sizeof的數組大小宏之前插入兩個“測試”來工作。 這些測試不影響最終計算,但旨在為非數組類型生成編譯錯誤:

  1. 除非arr是整數,枚舉,指針或數組,否則第一次測試失敗。 對於任何其他類型, reinterpret_cast<const T*>都應該失敗。
  2. 對於整數,枚舉或指針類型,第二次測試失敗。

    積分和枚舉類型將失敗,因為它們沒有匹配的check_type版本,因為check_type需要指針。

    指針類型將失敗,因為他們會匹配的模板版本check_type ,但返回類型( Is_pointer為模板) check_type是不完整的,這將產生一個錯誤。

    數組類型將通過,因為獲取類型為T的數組的地址將為您提供T (*)[] ,也就是指向數組的指針,而不是指向指針的指針。 這意味着check_type的模板化版本將不匹配。 感謝SFINAE ,編譯器將繼續使用非模板化版本的check_type ,它應該接受任何一對指針。 由於完全定義了非模板化版本的返回類型,因此不會產生錯誤。 由於我們現在不處理模板,因此本地類型工作正常。

這不是你想要的,但它很接近 - 來自winnt.h一個片段,其中包括對它所做的#$%^的一些解釋:

//
// RtlpNumberOf is a function that takes a reference to an array of N Ts.
//
// typedef T array_of_T[N];
// typedef array_of_T &reference_to_array_of_T;
//
// RtlpNumberOf returns a pointer to an array of N chars.
// We could return a reference instead of a pointer but older compilers do not accept that.
//
// typedef char array_of_char[N];
// typedef array_of_char *pointer_to_array_of_char;
//
// sizeof(array_of_char) == N
// sizeof(*pointer_to_array_of_char) == N
//
// pointer_to_array_of_char RtlpNumberOf(reference_to_array_of_T);
//
// We never even call RtlpNumberOf, we just take the size of dereferencing its return type.
// We do not even implement RtlpNumberOf, we just decare it.
//
// Attempts to pass pointers instead of arrays to this macro result in compile time errors.
// That is the point.
//
extern "C++" // templates cannot be declared to have 'C' linkage
template <typename T, size_t N>
char (*RtlpNumberOf( UNALIGNED T (&)[N] ))[N];

#define RTL_NUMBER_OF_V2(A) (sizeof(*RtlpNumberOf(A)))

RTL_NUMBER_OF_V2()宏最終用於更易讀的ARRAYSIZE()宏。

馬修威爾遜的“不完美的C ++”一書也討論了這里使用的技術。

如果您使用的是Microsoft平台,則可以利用_countof宏。 這是一個非標准擴展,它將返回數組中元素的數量。 它比大多數countof樣式宏的優點是,如果它在非數組類型上使用,它將導致編譯錯誤。

以下工作正常(VS 2008 RTM)

static int ARRAY[5];
enum ENUM{N=_countof(ARRAY)};

但再一次,它是MS特定的,所以這可能不適合你。

你不能解決它,這就是數組包裝器的原因之一,如boost數組 (當然還有stl風格的行為)。

在沒有具有當前C ++標准的宏的情況下,似乎無法將sizeof數組作為編譯時常量獲取(您需要一個函數來推導數組大小,但在需要編譯時常量的情況下不允許函數調用) 。 [編輯:但看看Minaev的精彩解決方案!]

但是,您的模板版本也不是類型安全的,並且遇到與宏相同的問題:它還接受指針,特別是數組衰減為指針。 當它接受指針時,sizeof(T *)/ sizeof(T)的結果無意義。

更好:

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

沒有C ++ 0x,我能得到的最接近的是:

#include <iostream>

template <typename T>
struct count_of_type
{
};


template <typename T, unsigned N>
struct count_of_type<T[N]> 
{
    enum { value = N };
};

template <typename T, unsigned N>
unsigned count_of ( const T (&) [N] )
{
    return N;
};


int main ()
{
    std::cout << count_of_type<int[20]>::value << std::endl;
    std::cout << count_of_type<char[42]>::value << std::endl;

    // std::cout << count_of_type<char*>::value << std::endl; // compile error

    int foo[1234];

    std::cout << count_of(foo) << std::endl;

    const char* bar = "wibble";

    // std::cout << count_of( bar ) << std::endl; // compile error

    enum E1 { N = count_of_type<int[1234]>::value } ;

    return 0;
}

它既可以為您提供可以傳遞變量的函數,也可以為您傳遞類型的模板。 您不能將該函數用於編譯時常量,但大多數情況下您知道該類型,即使僅作為模板參數。

現在可以使用STL庫來決定/選擇數組大小編譯時間

#include <iostream>
#include <array>

template<class T>
void test(T t)
{
    int a[std::tuple_size<T>::value]; // can be used at compile time
    std::cout << std::tuple_size<T>::value << '\n';
}

int main()
{
    std::array<float, 3> arr;
    test(arr);
}

輸出:3

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM