Reputation: 628
Both clang and gcc fail to compile the code below when ArrayCount is a template. This seems wrong, especially in light of the fact that the sizeof ArrayCount solution work. The template version of ArrayCount is normally a better solution, but it's getting in the way here and constexpr is seemingly not living up to the spirit of its promise.
#if 1
template<typename T, size_t N>
constexpr size_t ArrayCount(T (&)[N])
{
return N;
}
// Results in this (clang): error : static_assert expression is not an integral constant expression
// Results in this (gcc): error: non-constant condition for static assertion, 'this' is not a constant expression
#else
#define ArrayCount(t) (sizeof(t) / sizeof(t[0]))
// Succeeds
#endif
struct X
{
int x[4];
X() { static_assert(ArrayCount(x) == 4, "should never fail"); }
};
Upvotes: 0
Views: 273
Reputation: 477388
The right solution doesn't use homebrew code, but a simple type trait:
int a[] = {1, 2, 3};
#include <type_traits>
static_assert(std::extent<decltype(a)>::value == 3, "You won't see this");
Upvotes: 2
Reputation: 4601
It makes sense to me that this code would fail to compile since ArrayCount is a function taking a non-constexpr
argument. According to the standard, I believe this means that ArrayCount
must be intstantiated as a non-constexpr
function.
There are workarounds, of course. I can think of two off the top of my head (one implemented in terms of the other):
template<typename T> struct ArrayCount;
template<typename T, size_t N>
struct ArrayCount<T[N]> {
static size_t const size = N;
};
template<typename T>
constexpr size_t ArrayCount2() {
return ArrayCount<T>::size;
}
struct X {
int x[4];
X() {
static_assert(ArrayCount<decltype(x)>::size == 4, "should never fail");
static_assert(ArrayCount2<decltype(x)>() == 4, "should never fail");
}
};
It does mean having to use decltype()
when you might not wish to, but it does break the pro-forma constraint on taking a non-constexpr
parameter.
Upvotes: 1