Noah Roberts
12/16/2008 9:59:00 PM
Andrey Tarasevich wrote:
> Noah Roberts wrote:
>> Someone in another list posted this code. It compiles in G++ and
>> apparently is absorbed by comeau as well. VS vomits.
>
> A bug in VS prevents it from deducing the template argument properly in
> situations when const-array types are involved. The problem can be
> easily reproduced with
>
> template <class T> void foo(T& l, const T& r);
>
> int main() {
> int l[10];
> const int r[10] = {};
> foo(l, r);
> }
>
> I.e. the original problem with your code has nothing to do with the fact
> that 'assign' template is overloaded - it is easily reproducible with
> just one template.
>
> You can override the deduction process by specifying the argument
> explicitly (e.g. 'foo<int[10]>(l, r)' in my example), but, of course,
> this looks pretty ugly as a workaround.
>
> It is worth noting though that if you use that workaround (i.e. call
> your 'assign' as 'assign<Block< int, 16 > :: type>( a, b )' VS does
> correctly select the more specialized second version of 'assign'.
>
Thanks. I actually helped the guy figure this out a while back. I
think that MSVC is deducing "T" as "int[]" and then calling THAT
const...and not doing so correctly (in that it would cascade down to int
instead of int[]). My reply to them explaining this:
Noah Roberts wrote:
> In int const x[5][5], the int contained within the dual array is the
constant. In the case of T const& value[] though it is T that is const,
which is an array. This is nonsense I believe (arrays are always
r-values). VS seems to have no way to handle it while the others do.
I think this is a bug in VS.
At first I thought that what I was doing and what you are doing where
different because you had a typedef int[size][size] that you later
declare a const object of. I thought maybe this actually made the
*array* const but I was mistaken. The standard quite clearly states,
with example, that even when you are doing that to a typedef of array
type...the const applies to the elements, not to the array. The
provided example from 8.3.4/1:
typedef int A[5], AA[2][3];
typedef const A CA; // type is "array of 5 const int"
typedef const AA CAA; // type is "array of 2 array of 3 const int"
It doesn't get less ambiguous than that.
That means, to me, that even when T is an array type, expecting a const
reference of that type should resolve in the same manner and your
template should resolve. As someone mentioned in comp.lang.c++ your
second version (the one expecting an array) is a closer match and should
be the one used.
I think there's a bug in msvc in template typing. I'd bet that it's
considering const T (&ref)[size] to be an /array of const array of int/
when passed an /array of array of const int/, which is nonsense in C++
and would never resolve to anything.
My answer to their immediate problem:
Don't recall if it was said already but you can also explicitly say
which type you're using:
int main( )
{
Block< int, 16 > :: type a;
const Block< int, 16 > :: type b = { };
assign<int[16]>( a, b );
}
Then you'll need to establish template metaprograms to remove one array
level from a type and get the type beneath in order to write your
recursive template call so that it can cast the next level:
template < typename T >
struct next_level
{
typedef T type;
};
template < typename T, size_t sz >
struct next_level<T[sz]>
{
typedef T type;
};
template < typename T >
void assign( T & assignee, const T & value )
{
assignee = value;
}
template < typename T, std :: size_t sz >
void assign( T ( & assignee )[ sz ], const T ( & value )[ sz ] )
{
for (size_t i = 0; i < sz; ++i)
assign<typename next_level<T>::type>(assignee[i], value[i]);
}
Tested and works in msvc:
int main( )
{
typedef Block<int,3>::type type_t;
type_t a;
type_t const b = { { 1,2,3}, {4,5,6}, {7,8,9} };
assign<int[3]>( a, b );
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 3; ++j)
std::cout << a[i][j] << " ";
std::cout << std::endl;
}
std::cin.get();
}