Учитывая массив a
, Я хочу countof(a)
вывести количество элементов в массиве как константу времени компиляции. Если у меня есть указатель p
, Я хочу countof(p)
не компилировать. Кажется, что это должно быть (1) просто и (2) обычно покрыто SO, но (1) я не могу заставить его работать, и (2) поиск SO ничего не дал.
Вот моя попытка.
#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
}
Помогите?
template<typename T, std::size_t N>
constexpr std::size_t countof( T const(&)[N] ) { return N; }
проходит оба ваших теста. Там нет никакого способа, чтобы преобразовать int*
в T const(&)[N]
, поэтому отключение кода не требуется.
Чтобы расширить его, мы должны добавить:
template<typename T, std::size_t N>
constexpr std::size_t countof( std::array<T,N> const& ) { return N; }
Я мог бы даже поддаться искушению распространить его на вызов size()
для контейнеров. Хотя обычно это не время компиляции, единообразие может быть полезным:
for(int i=0; i<countof(c); ++i) {
// code
}
или что у тебя.
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;
что довольно многословно, но дает нам std::array
служба поддержки, std::initializer_list
поддержка, поддержка массивов в стиле C — все во время компиляции — и во время выполнения все стандартные контейнеры и строки countof
возможность. Если вы передадите указатель, вам скажут, что вызываемая вами функция delete
редактор
Я попытался создать static_assert
в этом случае, но столкнулся с проблемами с правилом разрешения, что любой template
должна иметь действительную специализацию. Я подозреваю, что вся проблема направлена в countof_impl
класс со специализацией на основе SFINAE может решить эту проблему.
Недостатком =delete
или же static_assert
Решение состоит в том, что перегрузка фактически существует для указателей. Если у вас этого нет, просто не существует действительной функции для вызова указателя: это ближе к истине.
Как это:
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;
Вы могли бы сделать это:
#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;
}
Если вам нужно сгладить все размеры, этот отрывок может быть под рукой
//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;
}
Пример использования:
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
Для тех из нас, кто должен использовать устаревшие компиляторы C ++ без C ++ 11-х constexpr
, будет работать следующее:
#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
}
Этот метод обеспечивает оценку времени компиляции countof
встраивая ADL в sizeof
оператор.