2013-02-05 12 views
6

Istnieje kilka dobrych powodów, aby woląLiczba tablic, które nie używają odr?

#include <cstdlib> 

template<typename T, std::size_t N> 
constexpr std::size_t ARRAY_COUNT_FUNC(T (&arr)[N]) { return N; } 

zamiast

#define ARRAY_COUNT_MACRO(arr) (sizeof(arr)/sizeof(*arr)) 

Jedną ważną różnicą jest to, że gdy wskaźnik (nie tablica) jest przekazywana do ARRAY_COUNT_MACRO, dyskretnie zwraca odpowiedź niepomocny, ale przechodząc ten sam argument ARRAY_COUNT_FUNC spowoduje błąd kompilatora wskazując na błąd.

Ale makro ma jedną zaletę: jego argument jest unevaluated.

#include <utility> 
struct S { 
    int member_array[5]; 
}; 

// OK: 
std::size_t count1 = ARRAY_COUNT_MACRO(std::declval<S&>().member_array); 

// ERROR: std::declval is odr-used! 
std::size_t count2 = ARRAY_COUNT_FUNC(std::declval<S&>().member_array); 

Czy istnieje inne podejście z zalet zarówno razem? I. e., Coś, co powoduje błąd kompilacji, jeśli argument nie jest tablicą i nie odr-używa jego argumentu.

Odpowiedz

6

Bezwstydnie oszukany z projektu Chromium, jak opisano here.

#include <utility> 
#include <cstdlib> 

template<typename T, std::size_t N> 
constexpr std::size_t ARRAY_COUNT_FUNC(T (&arr)[N]) { return N; } 

#define ARRAY_COUNT_MACRO(arr) (sizeof(arr)/sizeof(*arr)) 

// Template for typesafey goodness. 
template <typename T, size_t N> 
char (&ArraySizeHelper(T (&array)[N]))[N]; 
// sizeof to avoid actually calling the function. 
#define arraysize(array) (sizeof(ArraySizeHelper(array))) 

struct S { 
    int member_array[5]; 
}; 

int main() 
{ 

    // OK: 
    std::size_t count1 = ARRAY_COUNT_MACRO(std::declval<S&>().member_array); 

    // ERROR: std::declval is odr-used! 
    //std::size_t count2 = ARRAY_COUNT_FUNC(std::declval<S&>().member_array); 

    // OK: 
    std::size_t count2 = arraysize(std::declval<S&>().member_array); 

    // ERROR: 
    // int * p; 
    // std::size_t count3 = arraysize(p); 
} 
+3

Pan tylko wie, jak dawno temu pomysł został pierwszy wykluły, ale [Microsoft wykorzystuje zasadniczo ten sam algorytm] (http://blogs.msdn.com/b/the1/archive/2004/05 /07/128242.aspx) za '_countof (ar)' makro i ma co najmniej od '04. Szansa jest prawdopodobnie * bezszustowa * zerwana z kimś innym. – WhozCraig

2

... I wtedy przypomniałem sobie <type_traits> ma std::is_array szablonu. Innym rozwiązaniem:

#include <type_traits> 

template<typename T> 
constexpr auto ArrayCountImpl(std::nullptr_t) 
    -> typename std::enable_if<std::is_array<typename 
            std::remove_reference<T>::type>::value, 
           std::size_t>::type 
{ return std::extent<typename std::remove_reference<T>::type>::value; } 

template<typename T> 
std::size_t ArrayCountImpl(...) 
{ static_assert(!std::is_same<T,T>::value, 
       "Argument type is not an array"); } 

#define ARRAY_COUNT_MACRO_2(arr) (ArrayCountImpl<decltype(arr)>(nullptr)) 
Powiązane problemy