【问题标题】:Get size of an array-pointer template parameter获取数组指针模板参数的大小
【发布时间】:2014-04-20 19:50:16
【问题描述】:

我想知道是否可以自动推断作为模板参数传递的数组的大小,而无需(显式)传递其大小。

以下代码均在 g++ 4.8 和 clang++ 3.3 上编译无警告(使用 -std=c++11 -Wall)。

#include <iostream>

template<const int* arr>
struct array_container
{
    static constexpr int val = arr[1];
    array_container() {
        std::cout << val << std::endl;
    }
//  static constexpr int arr_size = ??;
};

constexpr int one[] = { 1 };
constexpr int two[] = { 1, 2 };

int main()
{
//  array_container<one> array_one;
    array_container<two> array_two;
//  (void) array_one;
    (void) array_two;
    return 0;
}

但是,如果我删除 main() 中的两个注释符号,我会得到两个编译器的越界错误。

现在,这很酷。尽管const int* arr 的类型是指针,但编译器不知何故知道数组的大小。有没有办法获得 arr 的大小,例如在array_container 中完成我的评论?

当然,你是不允许的

  • 使用任何宏
  • 将大小存储在 arr 中(例如,将 std::array 作为模板参数传递:constexpr std::array&lt;int, 1&gt; one = { 1 },或在字符串中使用像 '\0' 这样的结束标记)
  • 使用一个额外的模板参数来表示不能自动推断出的尺寸 (array_container&lt;1, one&gt; array_one)。

【问题讨论】:

  • 真的是编译器给你一个错误,还是运行时错误(崩溃)?
  • @JoachimPileborg 确实是编译器。我什至无法编译这个程序。
  • 你想做什么?
  • @Jefffrey 编辑:我想通过仅传递数组而不是(显式)传递数组大小来自动推断大小。
  • 我不认为array_container&lt;decltype(arr_a), arr_a&gt;::size 可以接受? :p

标签: c++ templates c++11


【解决方案1】:

也许你想要的是来自&lt;type_traits&gt; C++11 标准库标头的std::extent 模板:

#include <iostream>
#include <type_traits>

constexpr int one[] = { 1 };
constexpr int two[] = { 1, 2 };

int main()
{
    std::cout << std::extent<decltype(one)>::value << std::endl;
    std::cout << std::extent<decltype(two)>::value << std::endl;

    return 0;
}

输出:

1
2

【讨论】:

  • 问题询问是否可以“自动推断数组的大小,作为模板参数传递”。我的示例代码显示了它:您应该将数组传递给(自写)结构并在那里读取大小。您不会将数组传递给结构。所以你的答案是题外话。
  • @Johannes 好的,我明白了。我的解决方案和 badair 的解决方案都是编译时的,解决了查找数组大小的问题。但是您想将数组作为模板参数传递。你为什么要这样做?我认为您的解决方案对于所有可能的合理应用都过于复杂。
【解决方案2】:
template<size_t size>
constexpr size_t arraySize ( const int ( &arrayRef ) [size] ) {
    return size;
}

int main(){

    int A[1];
    int B[2];

    cout << arraySize(A) << arraySize(B);

    return 0;
}

我相信你正在寻找这样的东西,使用数组引用。声明数组引用的语法有点像函数指针的语法。此函数模板接受名为 arrayRef 的数组引用,它可以防止数组到指针的衰减,以便保留有关数组大小的编译时信息。如您所见,模板参数对编译器是隐含的。请注意,这仅在可以在编译时推断出大小时才有效。有趣的是,这应该仍然可以在没有命名 arrayRef 的情况下工作。为了使上面的模板更有用,你也可以添加一个模板参数来推断数组的类型。为了清楚起见,我把它省略了。

【讨论】:

  • 第一:不要使用int 作为大小(不要使用有符号整数)。请改用std::size_t。此外,您应该使用函数constexpr 来建议编译器在编译时计算数组大小。
  • arrayRef 可能是const: arraySize(const int (&amp;arrayRef)[size])
  • 您应该将数组作为模板参数传递给用户定义的结构(如问题中的示例代码所示)。但是,您将数组作为函数参数传递。
【解决方案3】:

可能不会,因为 SFINAE 只发生在即时上下文中,而该错误来自constexpr 中的 UB 导致编译时错误的要求,我认为这不是即时的。您可以尝试在 UB 上停止的递归 SFINAE,但即使它有效,您也必须检查标准并希望它不会改变(因为它相当晦涩和新)。

简单的方法是使用ise 的函数来推断数组大小,必须将其显式传递给类型,然后将其存储在auto 中。可能不是你想要的。

有一些建议允许从值参数中推导出类型参数,因此您可以等待这些建议。

不是一个可靠的答案,更多的是扩展评论,因此标记为社区 wiki。

【讨论】:

    【解决方案4】:

    确实有可能。我找到了使用 SFINAE 的解决方案。如果索引超出范围(本例中的第 3 行),它基本上会产生替换错误:

    template<class C>
    static yes& sfinae(typename val_to_type<
        decltype(*C::cont::data), *(C::cont::data + C::pos)>::type );
    template<class C>
    static no& sfinae(C );
    

    完整的源代码在github

    只有两个缺点:

    1. 你必须指定数组的类型(这是无法避免的)
    2. 它仅适用于 g++ 4.8.1 和 clang 3.3。 g++ 对空字符串失败(带有编译器错误)。如果有人可以测试其他编译器,那将不胜感激。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-23
    • 1970-01-01
    • 2018-09-08
    • 1970-01-01
    相关资源
    最近更新 更多