【问题标题】:How compute array size during compilation (without accepting pointers)?在编译期间如何计算数组大小(不接受指针)?
【发布时间】:2014-02-08 01:18:21
【问题描述】:

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

帮助?

【问题讨论】:

  • 为什么要编译? typename std::enable_if&lt;std::is_array&lt;T&gt;::value&gt;::type 将是错误的,因为 int 将失败 is_array
  • @remyabel:哇!你当然是对的。

标签: c++ arrays templates c++11 enable-if


【解决方案1】:
template<typename T, std::size_t N>
constexpr std::size_t countof( T const(&)[N] ) { return N; }

通过了您的两项测试。无法将int* 转换为T const(&amp;)[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 样式数组支持——所有这些都在编译时——而在运行时标准容器和字符串都是countofable。如果你传递一个指针,你会被告知你调用的函数是deleteed。

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

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

【讨论】:

  • 我同意此解决方案有效,但我真的很想将已删除的重载用于指针,以便用户收到“您正在调用已删除的函数”错误消息而不是“模板类型扣除失败”的错误信息。这就是导致我首先使用重载的原因。
  • @KnowItAllWannabe:也许你可以实际实现该函数并在其中放入一个static_assert,而不是deleted
  • initializer_list 的情况下,为容器调用 size() 可以是编译时的 - 仅从 C++14 开始。
  • @andyprowl 你能检测出size 是不是constexpr 吗?我们在乎吗?
  • @Yakk:不确定。可以在is_constexpr 上尝试 SFINAE,但我不知道有一种可靠的书写方式is_constexprHere 是一些尝试。不,我们不在乎。我只是说。
【解决方案2】:

像这样:

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;

【讨论】:

  • 'constexpr std::size_t countof(T) = delete;'更好 - return countof(...) 会产生额外的错误,否则
  • 甚至constexpr std::size_t countof(T*) = delete;(所以没有enable_if
  • @Jarod42 不,这会使数组调用不明确。
【解决方案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;
}

【讨论】:

  • 与 yuri Kilochek 的方法(目前在上面)相比,这种方法有哪些优势?如果您想为自定义错误消息使用 static_assert,您可以将其替换为解决方案中的 =delete。
  • @KnowItAllWannabe 它也适用于类型。不过,这可以通过不同的方式实现。
【解决方案4】:

如果你需要展平所有维度,这个摘录可以在手

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

【讨论】:

    【解决方案5】:

    对于我们这些必须使用旧版 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
    }
    

    此技术通过将 ADL 嵌入到 sizeof 运算符中来确保 countof 的编译时评估。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-11-09
      • 2016-06-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-30
      相关资源
      最近更新 更多