当我尝试将固定大小的数组传递给模板函数时,我的编译器表现得很奇怪。代码如下:
#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 N
到unsigned int N
。
最终草案ISO / IEC FDIS 14882:1998中的[dcl.type.simple
]部分似乎暗示“整数类型”是签名或未签字的:
signed
说明符强制char
对象和位字段进行签名;它与其他整数类型是多余的。
关于固定大小的数组声明,草案说[dcl.array
]:
如果常量表达式(
expr.const
)存在,它应该是一个整数常量表达式,其值应大于零。
那么,为什么我的代码使用明确的unsigned
大小类型,推断的signed
大小类型,但没有推断的unsigned
大小类型?
编辑 Serge想知道我需要第一个版本的位置。首先,这个代码示例显然是简化的。我的真实代码更精细一些。该数组实际上是另一个数组中的索引/偏移数组。 因此,从逻辑上讲,数组的类型应该与其大小类型相同,以获得最大的正确性。否则,我可能会遇到类型不匹配(例如,在unsigned int
和std::size_t
之间)。不可否认,这在实践中不应成为问题,因为编译器会隐式转换为两种类型中的较大者。
EDIT 2 我站得更正(谢谢,litb):大小和偏移当然是逻辑上不同的类型,特别是C数组的偏移属于std::ptrdiff_t
类型。
答案 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