如何静态断言 std::array 成员的大小

Posted

技术标签:

【中文标题】如何静态断言 std::array 成员的大小【英文标题】:How to static_assert the size of a std::array member 【发布时间】:2016-02-04 22:28:34 【问题描述】:

我想明确说明成员变量的数组大小限制,以防止其他人意外做出愚蠢的更改。以下天真的尝试将无法编译:

struct Foo

    std::array< int, 1024 > some_array;
    static_assert( (some_array.size() % 256) == 0, "Size must be multiple of 256" );
    //^ (clang) error: invalid use of non-static data member 'some_array'
;

即使std::array::size 是一个constexpr,我也不能像那样直接使用static_assert,因为函数和我的成员变量都不是静态的。

我想出的解决方案是使用decltype(因为我不想typedef数组)如下:

static_assert( (decltype(some_array)().size() % 256) == 0, "Size must be multiple of 256" );

这看起来像是在默认构造一个 rvalue,我不认为它是一个 constexpr

为什么会这样?

有没有更简洁的方法来实现静态断言?​​

【问题讨论】:

你的功能对我来说很好,先生 o.o,在 vusal 工作室。 (declval&lt;decltype(some_array)&gt;().size() % 256) == 0 不起作用? 【参考方案1】:

因为函数和我的成员变量都不是静态的。

没错。问题是静态断言不能引用非静态成员,因为some_array.size() 等价于this-&gt;some_array.size(),并且在类范围内没有this 指针(仅在函数声明符和默认成员初始化程序内部)。

但是,说 decltype(array_size) 是可以的,因为这实际上并不是在尝试引用对象 array_size 或调用其成员函数,它只是查询在类中声明的名称的类型。

这看起来像是在默认构造一个右值,我不认为它是一个 constexpr。

array&lt;int, N&gt; 是文字类型,因此可以用常量表达式构造。您构造右值的事实并不重要,您可以构造文字类型并在常量表达式中对其调用 constexpr 成员函数。

不能在那里使用像array&lt;std::string, N&gt; 这样的东西,因为std::string 不是文字类型,所以array&lt;string, N&gt; 也不是。

有没有更简洁的方法来实现静态断言?​​

标准特征std::tuple_size 专门用于std::array,因此您可以这样做:

static_assert( std::tuple_size<decltype(some_array)>::value % 256) == 0,
               "Size must be multiple of 256" );

【讨论】:

不幸的是,当您从std::array 继承并希望对派生类使用结构绑定时,这并不能解决问题。在这种情况下,您需要为派生类定义部分模板特化,因此不能依赖std::tuple_size【参考方案2】:

std::array::sizeconstexpr 并且应该与 static_assert 一起使用,但是它在这个特定的上下文中(即在类定义中)不起作用,因为 some_array 是一个非静态成员变量。

对于这个特定的上下文,您可以使用自制的类型特征在编译时获取大小,如下所示:

template<typename>
struct array_size;

template<typename T, std::size_t N>
struct array_size<std::array<T, N>> 
  static const std::size_t size = N;
;

static_assert 为:

static_assert(array_size<decltype(some_array)>::size % 256) == 0, "Size must be multiple of 256" );

Live Demo

【讨论】:

看起来你混淆了类型和值元函数:) 这就是我所期望的std::array 的实现方式......为什么不能直接访问模板的大小值?但事实并非如此。虽然这是一个很酷的解决方案,但我不确定它是否真的“更干净”,因为它似乎需要更多的框架。 using size = N; 无效,这将声明 typedef,但 N 不是类型。您可能希望 template&lt;typename T, std::size_t N&gt; struct array_size&lt;std::array&lt;T, N&gt;&gt; : std::integral_constant&lt;std::size_t, N&gt; ; 然后将 array_size&lt;A&gt;::value 定义为大小。但是,标准库中已经存在 std::tuple_sizestd::array 一起使用 @paddy,为了与其他容器保持一致,数组大小是成员函数,而不是静态常量。适用于 STL 风格容器的通用代码可以依赖begin()end()size() 等。

以上是关于如何静态断言 std::array 成员的大小的主要内容,如果未能解决你的问题,请参考以下文章

std::array<T, ?> 类型的成员变量

在基类中引用不同大小的 std::array 而不是 std::array

具有 std::array 大小类型的派生模板类

变长 std::array 像

cppcheck 超出范围

C++类的大小计算汇总