My compiler behaves oddly when I try to pass a fixed-size array to a template function. The code looks as follows:
#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)
}
It produces the following compile error in 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)]’
Note that the first call compiles and succeeds. This seems to imply that while int
is integral, unsigned int
isn't.
However, if I change the declaration of my above function template to
template <typename TSize, unsigned int N>
void f(TSize (& array)[N])
the problem just goes away! Notice that the only change here is from TSize N
to unsigned int N
.
Section [dcl.type.simple
] in the final draft ISO/IEC FDIS 14882:1998 seems to imply that an "integral type" is either signed or unsigned:
The signed
specifier forces char
objects and bit-fields to be signed; it is redundant with other integral types.
Regarding fixed-size array declarations, the draft says [dcl.array
]:
If the constant-expression (expr.const
) is present, it shall be an integral constant expression and its value shall be greater than zero.
So why does my code work with an explicit unsigned
size type, with an inferred signed
size type but not with an inferred unsigned
size type?
EDIT Serge wants to know where I'd need the first version. First, this code example is obviously simplified. My real code is a bit more elaborate. The array is actually an array of indices/offsets in another array. So, logically, the type of the array should be the same as its size type for maximum correctness. Otherwise, I might get a type mismatch (e.g. between unsigned int
and std::size_t
). Admittedly, this shouldn't be a problem in practice since the compiler implicitly converts to the larger of the two types.
EDIT 2 I stand corrected (thanks, litb): size and offset are of course logically different types, and offsets into C arrays in particular are of type std::ptrdiff_t
.
See Question&Answers more detail:
os