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 countof
able. If you pass a pointer, you are told that the function you call is delete
ed.
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.