Как вычислить размер массива во время компиляции (без учета указателей)?

Учитывая массив 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
}

Помогите?

8

Решение

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 Решение состоит в том, что перегрузка фактически существует для указателей. Если у вас этого нет, просто не существует действительной функции для вызова указателя: это ближе к истине.

7

Другие решения

Как это:

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;
3

Вы могли бы сделать это:

#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;
}
1

Если вам нужно сгладить все размеры, этот отрывок может быть под рукой

//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
1

Для тех из нас, кто должен использовать устаревшие компиляторы 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 оператор.

1
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector