获取数组指针模板参数的大小

时间:2014-03-15 11:42:14

标签: c++ templates c++11

我想知道我是否可以自动推断数组的大小,该数组作为模板参数传递,而不(明确地)传递其大小。

以下代码在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<int, 1> one = { 1 },或在字符串中使用结束标记,如'\0'
  • 不能自动推断array_container<1, one> array_one)的尺寸使用其他模板参数。

4 个答案:

答案 0 :(得分:1)

C ++ 11标准库的std::extent标题中的<type_traits>模板可能就是您想要的:

#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

答案 1 :(得分:0)

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

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

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

不是一个可靠的答案,更多的是扩展评论,所以标记为社区维基。

答案 2 :(得分:0)

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的情况下工作。要使上述模板更有用,您可以添加模板参数以推断出数组的类型。为了清楚起见,我把它留了出来。

答案 3 :(得分:0)

确实有可能。我找到了使用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 ++对空字符串失败(带有编译器错误)。如果有人可以测试其他编译器,那将不胜感激。