【问题标题】:Using std::extent on std::array在 std::array 上使用 std::extent
【发布时间】:2014-03-28 12:31:30
【问题描述】:

我有一个模板化函数,我想static_assert 它的类型大小为三。这段代码说明了我正在尝试做的事情,但不起作用:

template < typename T >
void foo( T& param )
{
    // This line is the one that I need to figure out how to write
    static_assert( 3 == std::extent< T >::value, "param must have a size of 3" );
}

int main( void )
{
    int cArray[3];
    std::array< int, 3 > stdArray;

    foo( cArray );
    foo( stdArray );
}

【问题讨论】:

  • 尝试通过引用传递?我不认为 C 数组喜欢这样按值传递。
  • 您应该解释它是如何“不起作用”的。你有错误吗?哪些?
  • 我认为他/她在编译时得到“param must have a size of 3”。
  • 谢谢 Neil Kirk 我确实错过了那里的&amp;。并且 DrD 是正确的静态断言触发。

标签: c++ arrays templates static-assert


【解决方案1】:

std::extent 是为内置数组定义的。对于std::array,请改用std::tuple_size。我不知道两者都有什么特点,但写一个很容易:

template<typename T>
struct array_size : std::extent<T> { };

template<typename T, size_t N>
struct array_size<std::array<T,N> > : std::tuple_size<std::array<T,N> > { };

这是您的 foo,已更正/概括 (live example):

template < typename T >
void foo( T&& param )
{
    using U = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
    static_assert( 3 == array_size<U>::value, "param must have a size of 3" );
}

首选通用引用T&amp;&amp; param,否则只能使用左值。

【讨论】:

  • 确实如此,但即便如此,他还是会继续遇到cArray这个内置数组的错误
  • @DrD 为什么会这样?它的大小在编译时是已知的,对吧?
  • @aschepler 你是对的,不是在更改 T& 参数之后。问题在于,正如 OP 定义的那样,foo 接收到数组的副本。在任何情况下,尽管 T& 错误仍然存​​在,因为 std::array 不被 std::extent 接受
  • @DrD 好吧,对于这种类型规范化,您需要使用std::remove_reference,后跟std::remove_cv。将这些嵌入到类型特征中并不是一个好主意。
  • foo(T param) 将接收指向数组第一个元素的指针,而不是数组的副本。并且 OP 已被编辑。
【解决方案2】:

这建立在iavr 的解决方案之上。

template < typename T >
void foo( T& param )
{
    static_assert( 3 == ( std::is_array< T >::value ? std::extent< T >::value : std::tuple_size< T >::value ), "param must have a size of 3" );
}

【讨论】:

  • 改正自己的错误后,好像可以了!非常优雅的解决方案,恭喜!
  • 不错。另一种选择可能是定义template&lt;typename T&gt; using array_size = std::integral_constant&lt;size_t, std::is_array&lt; T &gt;::value ? std::extent&lt; T &gt;::value : std::tuple_size&lt; T &gt;::value&gt;;。
  • @iavr 我在你的回答中也看到了using 关键字。你能解释一下这是在做什么吗?
  • @JonathanMee 参见type alias, alias template(C++11 起)。前者(如我的回答)只是typedef 的替代语法。后者(如我之前的评论)是typedef(大致)什么是普通类的类模板。如果您使用类型,这将非常方便。
  • @iavr 哇,好的,所以我可以使用static_assert( 3 = array_size&lt; T &gt;, "param must have a size of 3" ); 或者我什至可以在std::enable_if 中使用它吗?我相信我对类型别名的理解已经足够好了,但我仍在尝试找出std::integral_constant 和别名模板。无论如何,如果您想将其编辑到您的答案中,我很乐意接受。
【解决方案3】:

我想不出一种方法来编写一个同时处理两者的函数,但这就是重载的目的。

template <std::size_t N, typename T, std::size_t Bound>
void check_array_size( T (&)[Bound] )
{
    static_assert(Bound == N, "incorrect array size");
}

template <std::size_t N, typename T, std::size_t Bound>
void check_array_size( const std::array<T,Bound>& )
{
    static_assert(Bound == N, "incorrect array size");
}

template <std::size_t N>
void check_array_size( ... )
{
    static_assert(N<0, "argument is not an array");
}

template <typename T>
void foo(T& param)
{
    check_array_size<3>(param);
    // actual function implementation...
}

【讨论】:

    【解决方案4】:

    我怀疑您遇到此错误是因为在编译时(即评估 *static_assert* 时)cArray 和 stdArray 的维度是未知的,并且 std::extent 返回零:

    http://en.cppreference.com/w/cpp/types/extent

    实际上,如果将条件更改为等于零,错误就会消失:

    static_assert( std::extent< T >::value == 0, "param is not zero" );
    

    尽管如此,正如其他帖子中已经指出的那样,std::extent 不适用于 std::array,仅适用于内置数组,例如作为 cArray

    在 OP 的编辑之后: 现在您已修改 foo 以接受引用,您将拥有 std::extent 为 cArray 报告 3 和 0(零)对于 stdArray。因此,您将在编译时继续出现异常,因为 stdArray 的维度不是 3。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-04-01
      • 1970-01-01
      • 2016-05-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-31
      • 2017-04-17
      相关资源
      最近更新 更多