2013-02-05 8 views
6

odr-useではない配列数?

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

つを好むために、いくつかの理由がありますが、重要な違いは、ポインタ(ない配列)がARRAY_COUNT_MACROに渡されたとき、それは静かに役に立たない答えを返すことで、同じ引数をARRAY_COUNT_FUNCに渡すと、間違いを指摘するコンパイラエラーが発生します。

マクロには利点があります。その引数は評価されていません。

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

両方の利点を併せ持つアプローチがありますか?つまり、引数が配列ではない場合にコンパイルエラーを引き起こし、引数をodr-useしないものです。

答えて

6

hereと記載されているように、Chromiumプロジェクトから恥知らずにリッピングされました。

#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

主は唯一のアイデアが最初に孵化したどのくらい前に知っているが、([Microsoftは、本質的に同じアルゴリズムを使用しています] http://blogs.msdn.com/b/the1/archive/2004/05 /07/128242.aspx)に '_countof(ar)'マクロを追加したもので、少なくとも'04以降です。チャンスは、おそらく彼らは誰か他の人から*恥知らず*リッピングしているでしょうか。 – WhozCraig

2

...そして私は<type_traits>std::is_arrayテンプレートを持って思い出しました。別の解決策:

#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)) 
関連する問題