Question

Given an array a, I want countof(a) to yield the number of elements in the array as a compile-time constant. If I have a pointer p, I want countof(p) to not compile. This seems like it should be (1) straightforward and (2) commonly covered in SO, but (1) I can't get it to work, and (2) searching SO didn't turn up anything.

Here's my attempt.

#include <cstddef>
#include <type_traits>

template<typename T, std::size_t n,
         typename = typename std::enable_if<std::is_array<T>::value>::type>
constexpr std::size_t countof(T (&)[n]) { return n; }

template<typename T, 
         typename = typename std::enable_if<std::is_pointer<T>::value>::type>
void countof(T*) = delete;

int main()
{
  int a[10];
  auto asize = countof(a);             // should compile
  static_assert(countof(a) == 10,
                "countof(a) != 10!");

  int *p;
  auto psize = countof(p);             // shouldn't compile
}

Help?

No correct solution

OTHER TIPS

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

passes both of your tests. There is no way to convert an int* into a T const(&)[N], so no disabling code is needed.

To extend it we should add:

template<typename T, std::size_t N>
constexpr std::size_t countof( std::array<T,N> const& ) { return N; }

I might even be tempted to extend it to calling size() for containers. While it won't usually be compile-time, the uniformity might be useful:

for(int i=0; i<countof(c); ++i) {
  // code
}

or what have you.

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

template<typename T> struct type_sink { typedef void type; };
template<typename T> using TypeSink = typename type_sink<T>::type;
template<typename T, typename=void>
struct has_size : std::false_type {};
template<typename T>
struct has_size<T, TypeSink< decltype( std::declval<T>().size() ) > >:
  std::true_type
{};
template<bool b, typename T=void>
using EnableIf = typename std::enable_if<b,T>::type;

template<typename T>
constexpr
EnableIf<has_size<T const&>::value,std::size_t>
countof( T const& t ) {
  return t.size();
}
// This is optional.  It returns `void`, because there
// is no need to pretend it returns `std::size_t`:
template<typename T>
constexpr
EnableIf<std::is_pointer<T>::value>
countof( T const& t ) = delete;

which is pretty verbose, but gives us std::array support, std::initializer_list support, C-style array support -- all at compile time -- and at run time standard containers and strings are all countofable. If you pass a pointer, you are told that the function you call is deleteed.

I attempted to create a static_assert in that case, but ran into problems with the resolution rule that any template must have a valid specialization. I suspect routing the entire problem into a countof_impl class with SFINAE based specializations might fix that problem.

A downside to the =delete or static_assert solution is that an overload actually exists for pointers. If you don't have that, then there simply is no valid function to call that takes a pointer: this is closer to the truth.

Like this:

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

template <typename T, typename = typename std::enable_if<std::is_pointer<T>::value>::type>
constexpr std::size_t countof(T) = delete;

You might do this:

#include <iostream>
#include <type_traits>

namespace Detail {
    template <typename T>
    struct array_size {
        // A simple false is no good
        static_assert(std::is_array<T>::value, "No Array");
    };

    template <typename T, std::size_t N>
    struct array_size<T[N]> {
        static constexpr std::size_t value = N;
    };
}

template <typename T>
constexpr std::size_t array_size() {
    return Detail::array_size<T>::value;
}

template <typename T>
constexpr std::size_t array_size(const T&) {
    return Detail::array_size<T>::value;
}

int main(){
    typedef int A[3];
    typedef char B[array_size<A>()];
    A a;
    std::cout << array_size<A>() << array_size(a) << array_size<B>() << std::endl;
    // int* p = a;
    // error: static assertion failed: No Array
    // std::cout << array_size(p) << std::endl;
    return 0;
}

If you need to flatten all dimensions, this excerpt can be in hand

//Moving to detail like 'Dieter Lücking'
namespace detail {

    /*recurse over ranks*/
    template <typename A, size_t R = std::rank<A>::value>
    struct aux {
        static constexpr size_t value =
        std::extent<A, 0>::value * aux<typename std::remove_extent<A>::type>::value;
    };

    /*stop condition*/
    template <typename A>
    struct aux<A, 0> {
        static constexpr size_t value = 1;
    };
}

/*convenient function, updated to use enable_if, is_array*/
template <typename A, typename = typename std::enable_if<std::is_array<A>::value>::type>
constexpr size_t countof(A const &) {
    return detail::aux<A>::value;
}

Use example:

int a[][3][3] = {
    {{1,2,3},
     {1,2,3},
     {1,2,3}},
    {{1,2,3},
     {1,2,3},
     {1,2,3}}
};
int b[countof(a)]; //size 2*3*3*1 = 18

For those of us who have to use legacy C++ compilers without C++11's constexpr, the following will work:

#include <cstddef>

template <class T, size_t N> char (*countof(T(&)[N]))[N]; // declaration only
#define countof(x)          sizeof(*countof(x))

int main()
{
  int a[10];
  size_t asize = countof(a);             // should compile
  static_assert(countof(a) == 10,
               "countof(a) != 10!");
  int *p;
  size_t psize = countof(p);             // shouldn't compile
}

This technique ensures the compile-time evaluation of countof by embedding the ADL within the sizeof operator.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top