9

给定一个数组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
}

帮助?

4

5 回答 5

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

通过你的两个测试。无法将 a 转换int*为 a 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可以。如果你传递一个指针,你会被告知你调用的函数是deleteed。

在这种情况下,我尝试创建一个static_assert,但遇到了任何template必须具有有效专业化的解析规则的问题。我怀疑将整个问题路由到countof_impl具有基于 SFINAE 的专业化的类中可能会解决该问题。

=deleteor解决方案的一个缺点static_assert是指针实际上存在重载。如果你没有那个,那么根本就没有可以调用的有效函数接受一个指针:这更接近事实。

于 2014-01-18T17:37:57.037 回答
3

像这样:

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;
于 2014-01-18T17:52:22.073 回答
1

对于我们这些必须使用没有C++11 的旧版 C++ 编译器的人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

于 2014-05-27T04:21:41.803 回答
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
于 2014-01-18T18:06:10.377 回答
1

你可以这样做:

#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;
}
于 2014-01-18T17:53:01.187 回答