this code fails to compile on EDG based compilers:
template <typename T, int SIZE>
int test(const T (&)[SIZE]) { return SIZE; }
int main() {
int array[10],
s = test(array);
}
// error: no instance of function template "test" matches the argument list
// argument types are: (int [10])
The problem here seems to be the combination of array size deduction with a
const ref parameter (everything works fine if I remove the const
qualifier!). Can anybody tell me the reason for this behaviour?
Thanks in advance,
Stefan
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
The current language doesn't say anything about cv-qualified conversions
between pointers/references to array types although it seems to be safe as
cv-qualified pointers conversion (can't break the type system).
There is an open DR about this issue:
http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/cwg_active.html#330
Notice that VC7.1 and BCC5.5 accept your code.
You might be interesting in the following more effective (compile time) way
to calculate array length:
template <typename T, int SIZE>
char (& lengthof(T (&)[SIZE]) )[SIZE];
#define lengthof(arr) sizeof(lengthof(arr))
int array[10];
typedef char test[lengthof(array) == 10];
Rani
Could you explain that code, please?
And why simply leaving out 'const' isn't good enough?
TIA.,
- Alf
"Rani Sharoni" <rani_s...@hotmail.com> wrote:
>
> The current language doesn't say anything about cv-qualified
conversions
> between pointers/references to array types although it seems to be
safe as
> cv-qualified pointers conversion (can't break the type system).
>
I think that's not really true, especially as there are strict rules for
type deduction in function template calls. Take a look at [14.8.2.1]
where
it says:
"If P is a cv-qualified type, the top level cv-qualifiers of P's type
are
ignored for type deduction.
If P is a reference type, the type referred to by P is used for type
deduction."
(P = function template parameter type)
The "decaying rules" (for conversions array -> pointer) are applied only
"if
P is not a reference type".
There are no DRs on these lines, so I think EDG behaves incorrectly
here.
> You might be interesting in the following more effective (compile
time)
way
> to calculate array length:
I knew about that, but in this case I HAD to pass the array to a
function
and to deduce the array size.
Regards,
Stefan
I think the reason is exactly what it says, providing that almost all
type
conversions are not allowed as a part of deduction process.
Here's an example.
template<typename T, int SIZE>
void test(const T (&)[SIZE]); // const is explicit
int main()
{ const int arr1[]={1,2,3};
int arr2[]={1,2,3};
test(arr1); // OK, with T=int and SIZE=3
// arr1 matches (const int (&)[3])
test(arr2); // Error, whatever T might be, (const T) never matches
(int)
in arr2;
// Even though type (int [3]) of arr2
// can be converted to (const int(&)[3])
// it's never taken into account during deduction.
}
Another example.
template<typename T, int SIZE>
void test(T (&)[SIZE]); // no const-ness
int main()
{ const int arr1[]={1,2,3};
int arr2[]={1,2,3};
test(arr1); // OK, with T=const int and SIZE=3
// arr1 matches (const int (&)[3])
test(arr2); // Ok, with T=int and SIZE=3
}
Notice the difference between (const T) in the first example and
(T=const
int) in the second.
Nikolai Borissov
there is a slightly more portable way of achieving the same:
template< std::size_t sz >
class size
{
char give_size[sz];
};
template< typename T, std::size_t sz >
size<sz> sizer( const T (&array)[sz] );
int main()
{
int a[] = {1,2,3,4,5};
cout << sizeof( sizer(a) ) << endl;
return 0;
}
regards
Thorsten , Aalborg University
> >You might be interesting in the following more effective (compile
time)
way
> >to calculate array length:
> >
> >template <typename T, int SIZE>
> >char (& lengthof(T (&)[SIZE]) )[SIZE];
This is a function lengthof that takes a reference to an array of type T
and
length SIZE or T[SIZE], and returns a reference to an array of type char
and
the same length of char[SIZE].
In C++ char always has sizeof 1. Therefore the sizeof of the array
char[SIZE] is SIZE.
If you say sizeof(function) the compiler finds the sizeof of the
function's
return type. Furthermore that return type is a compile time constant
and
thus can be used in constant expressions -- for example in declaring
static
arrays or template argument types. For example,
int i[10];
double d[sizeof(lengthof(i))]; // same as d[10]
If you wanted to change the length of the array to 20 you just change
the
i[10] to i[20]. Just one place to change :).
Of course, in most cases the following approach is clearest and to be
preferred in most (but not all) cases
const int N = 10;
int i[N];
double d[N];
Note that as an alterative to lengthof we could say
int i[10];
double d[sizeof(i)/sizeof(*i)]; // same as d[10]
It's simpler than the sizeof thing and I suspect more common, though has
one
disadvantage. When there are many arrays we could by accident end up
writing
double d[sizeof(i)/sizeof(*j)];
For example we rename an array and forget to change all the places that
reference the array. Or maybe we have too many arrays.
> >#define lengthof(arr) sizeof(lengthof(arr))
> >
> >int array[10];
> >
> >typedef char test[lengthof(array) == 10];
This last statement is essentially a compile time assert to verify that
array has length 10. If the array does not have length 10 then you get
an
error that arrays of length 0 are not allowed, which you have to know
means
that the compile time assertion failed.
Though I'm confused about the use of lengthof as both a pre-processor
define
as well as function name. Maybe someone can educate me on this?
Thanks.
--
+++++++++++
Siemel Naran
> And why simply leaving out 'const' isn't good enough?
RFF (Request For Flames) on lengthof() by Andrei Alexandrescu:
http://tinyurl.com/bj7y
Rani
I was wrong about the conversion. The conversion from T to T cv& (or T
cv*)
is obviously allowed even when T is of array type.
The DR link that I mentioned isn't relevant. Sorry.
Back to your original question.
I compiled the code also with GCC3.2 and VC7.1. GCC rejected the code
while
VC accepted it.
> I think that's not really true, especially as there are strict rules
> for type deduction in function template calls. Take a look at
> [14.8.2.1] where it says:
I looked at 14.8.2 (especially 14.8.2.1/3) and I didn't see any
treatment in
the case (conversion) you presented. Notice that the following actually
works (EDG and GCC):
template<typename T> int test(T const&);
In this case T==int[10] and test<int[10]> is instantiated (per
14.8.2.1/3).
> There are no DRs on these lines, so I think EDG behaves incorrectly
> here.
I'm really not authorized to say that but maybe Daveed Vandevoorde can
help
us with this issue.
Regards,
Rani
But taking away the cv modifiers is explicitly specified.
As you can see here, the difference is just the array
template <typename T>
void test(const T &) {}
template <typename T>
void test_fixed(const T (&)[10]) {}
template <typename T>
int asz(const T (&)[10]) { return 0; }
int main() {
int array[10];
//int s = asz(array); // this would not work
int x;
test(x); // this works!
test(array); // this also works
//test_fixed(array); // this doesn't again
}
Regards,
Stefan
> > And why simply leaving out 'const' isn't good enough?
> RFF (Request For Flames) on lengthof() by Andrei Alexandrescu:
> http://tinyurl.com/bj7y
>
Unfortunately, all these approches can't help if I want to pass a fixed size
array to a function (maybe a static definition table!) and I want to know
automatically how large it is. I think the only way to do this is to deduce
the size and provide it as template parameter. The major disadvantage is the
code increase if you have many tables with different sizes.
Consider this search function as an example:
template <typename T, int SIZE, typename COMPARE>
inline T* SearchFunction(T (&table)[SIZE], const std::string&
sSomethingToSearch, COMPARE *compare)
{
return std::lower_bound(&table[0], &table[SIZE - 1], sSomethingToSearch,
compare);
}
(where table[SIZE-1] has to be a dummy end iterator)
SearchFunction allows you to pass just the table instead of two iterators.
Regards,
Stefan
But the following works on both Intel C++ 7.1 and g++ 3.2:
template<typename T>
void test(const T *) {}
int main()
{
const int arr1[]={1,2,3};
int arr2[]={1,2,3};
test(arr1);
test(arr2);
}
What's the difference with regard to constness?
Regards,
Terje
As I saw in your quotation of the Standard this applies to top level
cv-qualifiers. In the type _const T (&)[10]_ const is not a top-level
one. The top level of this type is an array which is always constant, and
_const_ cannot be specified for an array directly. Indirectly it can be done
like this:
typedef const int arr_type[10]; // this const applies to array's elements
void test(const arr_type); // this const is a top level qualifier
void test(const arr_type&); // this is top level too? (I think, yes)
Nikolai Borissov
Well, after reading of the Standard I realize that things are more
complicated than I thought.
But first, your question.
With pointers it's all clear and according to the Standard.
The following conversions are used here:
- array-to-pointer;
- qualification conversion.
These conversions are allowed as part of deduction process and occur in
succession:
const int [] -> const int * - for arr1, only array-to-pointer conversion
is necessary
int [] -> int* -> const int* - for arr2, array-to-pointer following by a
qualification conversion
Now go back to references to arrays.
According to the Standard, top level cv-qualifiers for arrays are applied to
its elements. Let's assume that reverse is true too (though I couldn't find
an explicit statement in the Standard), i.e. cv-qualifiers of array elements
should be considered as top level qualifiers for arrays, removing them from
elements. This assumption is confirmed by the following example, which is
successfully compiled by online Comeau C++:
void func(const int (&)[3]);
void test() { int arr[3]; func(arr);}
Type int[3] of arr is converted to const int[3] parameter type.
Because there are no qualification conversions for arrays, the only way to
formally succeed in conversion is to consider const qualifier as a top level
one, rather than a qualifier for the element. This way it can be
automatically ignored.
However, this assumption doesn't work in templetized example:
template<typename T> void func(const T (&)[3]);
void test() { int arr[3]; func(arr);}
Apparently the const qualifier here always goes with template parameter
type T. Because it is not a top level one it cannot be ignored.
As a result, int type of arr element never matches const T (T=int).
I don't know the reason why it happens here, may be due to some
general complications of a type composition.
Nikolai Borissov
Like array, a reference is intrinsically constant, so the last example is
also "const" (or what to call it), with or without the "const", there. The
"const" given applies to the referee, not reference, so it isn't top-level.
Regards,
Terje
THORSTEN OTTOSEN schrieb:
> > You might be interesting in the following more effective (compile
> time)
> way
> > to calculate array length:
> >
> >
> >
> > template <typename T, int SIZE>
> > char (& lengthof(T (&)[SIZE]) )[SIZE];
>
> there is a slightly more portable way of achieving the same:
>
> template< std::size_t sz >
> class size
> {
> char give_size[sz];
> };
>
> template< typename T, std::size_t sz >
> size<sz> sizer( const T (&array)[sz] );
>
> int main()
> {
> int a[] = {1,2,3,4,5};
> cout << sizeof( sizer(a) ) << endl;
>
> return 0;
> }
Is this really portable?
Is there a guarantee that class "size"'s size won't be greater than its
sole member, for example because of padding bytes?
regards,
Thomas
OK. Just work your way through 14.8.2.1.
Para 1: Our A is "int[10]" and our P is "T const(&)[S]".
Para 2:
P is a reference type, so skip the 3 bullets.
P is not a cv-qualified type (references never are),
so skip the first sentence after the bullets.
P is a reference type, so we use P' = "T const [S]"
for deduction purposes.
Para 3:
We try to find T, S such that P' becomes identical
to A. That's not possible, so we looks at the
three bullets. The 2nd and 3rd don't apply, but
the first implies that T=int, S=10 is OK because
"int const[10]" is more cv-qualified that "int[10]"
(see 3.9/5).
So deduction succeeds. Now you've got to check that
the resulting specialization is viable (it is; left
as an exercise to the reader <grin>), and selected
by overload resolution (trivial; there is but one
viable candidate).
So yes, this is valid code and an EDG bug (known).
Daveed