views:

878

answers:

3

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); // *
}

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)]’

For reference, line 15 is marked by (*) in the code above. 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.

+1  A: 

Visual Studio 2008 compiles just fine. What compiler are you using ?

davidnr
Sorry; I've just updated my question.
Konrad Rudolph
+7  A: 

Hmm, the Standard says in 14.8.2.4 / 15:

If, in the declaration of a function template with a non-type template-parameter, the non-type template-parameter is used in an expression in the function parameter-list and, if the corresponding template-argument is deduced, the template-argument type shall match the type of the template-parameter exactly, except that a template-argument deduced from an array bound may be of any integral type.

Providing this example:

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
}

That suggests that the compilers that fail to compile your code (apparently GCC and Digital Mars) do it wrong. I tested the code with Comeau, and it compiles your code fine. I don't think there is a different to whether the type of the non-type template parameter depends on the type of the type-parameter or not. 14.8.2.4/2 says the template arguments should be deduced independent from each other, and then combined into the type of the function-parameter. Combined with /15, which allows the type of the dimension to be of different integral type, i think your code is all fine. As always, i take the c++-is-complicated-so-i-may-be-wrong card :)

Update: I've looked into the passage in GCC where it spits out that error message:

  ...
  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);
    }
  ...

It seems to have missed to mark the type of the size as dependent in an earlier code block. As that type is a template parameter, it is a dependent type (see 14.6.2.1).

Update: GCC developers fixed it: Bug #38950

Johannes Schaub - litb
I think that you are probably right. Thanks for looking up the relevant passage.
Konrad Rudolph
litb, have you filed a bug report or submitted a patch? This should probably be done. I admit freely that I am intimidated by the kraken GCC project and have no idea how to do either.
Konrad Rudolph
Konrad Rudolph, i thought i would leave the job for you so you can earn the fame, but if you don't have the time or don't want to do it, i'll do it. Just leave another comment in that case.
Johannes Schaub - litb
Looks like this is related: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=33553
Johannes Schaub - litb
i've reported it: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=38950
Johannes Schaub - litb
Thanks a lot. I appreciate that.
Konrad Rudolph
Re bugfix: This is totally cool! Is there a statistic of how many StackOverflow discussions resulted in bug fixes? I know of at least one other.
Konrad Rudolph
+1  A: 

Well the first example compiles just fine with Microsoft compiler (2005).

Digital Mars complains about

"Error: integral expression expected"

in line

void f(TSize (& array)[N]) {

Can you give an example where you absolutly have to use this first version?

Serge
Although, that's not really an answer but I post it on Konrad Rudolph's behalf (see comments of the question)
Serge
Thanks for complying. See, you even got a vote for your efford. ;-) I think this way is ultimately more searchable and thus user-friendlier if someone else has a similar problem and searches the web.
Konrad Rudolph