An array that does not use odr?

There are several good reasons to choose.

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

but not

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

One important difference is that when a pointer (not an array) is passed to ARRAY_COUNT_MACRO , it silently returns a useless answer, but passing the same argument to ARRAY_COUNT_FUNC will result in a compiler error indicating an error.

But a macro has one advantage: its argument is not evaluated.

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

Is there a different approach with the benefits of both together? I. e., Which causes a compilation error if the argument is not an array and does not use an odr argument.

+6
source share
2 answers

Shamelessly breaks down from the Chromium project, as described 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); } 
+6
source

... And then I remembered that <type_traits> has a std::is_array . Another solution:

 #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)) 
+2
source

All Articles