如何将数组大小作为模板传递模板类型?

时间:2009-01-23 10:56:19

标签: c++ arrays templates type-inference

当我尝试将固定大小的数组传递给模板函数时,我的编译器表现得很奇怪。代码如下:

#include <algorithm>
#include <iostream>
#include <iterator>

template <typename TSize, TSize N>
void f(TSize (& array)[N]) {
    std::copy(array, array + N, std::ostream_iterator<TSize>(std::cout, " "));
    std::cout << std::endl;
}

int main() {
    int x[] = { 1, 2, 3, 4, 5 };
    unsigned int y[] = { 1, 2, 3, 4, 5 };
    f(x);
    f(y); //line 15 (see the error message)
}

它在GCC 4.1.2中产生以下编译错误:

test.cpp|15| error: size of array has non-integral type ‘TSize’
test.cpp|15| error: invalid initialization of reference of type
  ‘unsigned int (&)[1]’ from expression of type ‘unsigned int [5]’
test.cpp|6| error: in passing argument 1 of ‘void f(TSize (&)[N])
  [with TSize = unsigned int, TSize N = ((TSize)5)]’

请注意,第一个呼叫编译并成功。这似乎意味着虽然int是不可或缺的,但unsigned int不是。

但是,如果我将上述功能模板的声明更改为

template <typename TSize, unsigned int N>
void f(TSize (& array)[N])
问题就消失了!请注意,此处唯一的更改是从TSize Nunsigned int N

最终草案ISO / IEC FDIS 14882:1998中的[dcl.type.simple]部分似乎暗示“整数类型”是签名或未签字的:

  

signed说明符强制char对象和位字段进行签名;它与其他整数类型是多余的。

关于固定大小的数组声明,草案说[dcl.array]:

  

如果常量表达式expr.const)存在,它应该是一个整数常量表达式,其值应大于零。

那么,为什么我的代码使用明确的unsigned大小类型,推断的signed大小类型,但没有推断的unsigned大小类型?

编辑 Serge想知道我需要第一个版本的位置。首先,这个代码示例显然是简化的。我的真实代码更精细一些。该数组实际上是另一个数组中的索引/偏移数组。 因此,从逻辑上讲,数组的类型应该与其大小类型相同,以获得最大的正确性。否则,我可能会遇到类型不匹配(例如,在unsigned intstd::size_t之间)。不可否认,这在实践中不应成为问题,因为编译器会隐式转换为两种类型中的较大者。

EDIT 2 我站得更正(谢谢,litb):大小和偏移当然是逻辑上不同的类型,特别是C数组的偏移属于std::ptrdiff_t类型。

1 个答案:

答案 0 :(得分:13)

嗯,标准在14.8.2.4 / 15中说:

  

如果在具有非类型模板参数的函数模板的声明中,在函数参数列表的表达式中使用非类型模板参数,并且如果推导出相应的模板参数,模板参数类型应与模板参数的类型完全匹配,除了从数组绑定推导出的模板参数可以是任何整数类型。

提供此示例:

template<int i> class A { /* ... */ };
template<short s> void f(A<s>);
void k1() {
    A<1> a;
    f(a);    // error: deduction fails for conversion from int to short
    f<1>(a); // OK
}

这表明未能编译代码的编译器(显然是GCC和Digital Mars)做错了。我用Comeau测试了代码,它编译好你的代码。我认为非类型模板参数的类型是否取决于type-parameter的类型是否有所不同。 14.8.2.4/2表示模板参数应该相互独立推导,然后组合成函数参数的类型。结合/ 15,它允许维度的类型具有不同的整数类型,我认为你的代码都很好。像往常一样,我采取c ++ - 很复杂 - 所以我可能是错误的卡:)

更新:我查看了GCC中的那段文字,其中显示了错误消息:

  ...
  type = TREE_TYPE (size);
  /* The array bound must be an integer type.  */
  if (!dependent_type_p (type) && !INTEGRAL_TYPE_P (type))
    {
      if (name)
    error ("size of array %qD has non-integral type %qT", name, type);
      else
    error ("size of array has non-integral type %qT", type);
      size = integer_one_node;
      type = TREE_TYPE (size);
    }
  ...

似乎错过了将大小的类型标记为依赖于较早的代码块。由于该类型是模板参数,因此它是一种依赖类型(请参阅14.6.2.1)。

更新: GCC开发人员修复了它:Bug #38950