legal code?

N

Noah Roberts

Someone in another list posted this code. It compiles in G++ and
apparently is absorbed by comeau as well. VS vomits.

#include <cstddef>

template < typename T, std :: size_t sz >
struct Block
{
typedef T type[ sz ][ sz ];
};

template < typename T >
void assign( T & assignee, const T & value )
{}

template < typename T, std :: size_t sz >
void assign( T ( & assignee )[ sz ], const T ( & value )[ sz ] )
{}

int main( )
{
Block< int, 16 > :: type a;
const Block< int, 16 > :: type b = { };

assign( a, b );
}


1>e:\dev_workspace\experimental\boost_msg_test\boost_msg_test\boost_msg_test.cpp(22)
: error C2782: 'void assign(T (&)[sz],const T (&)[sz])' : template
parameter 'T' is ambiguous
1>
e:\dev_workspace\experimental\boost_msg_test\boost_msg_test\boost_msg_test.cpp(14)
: see declaration of 'assign'
1> could be 'const int [16]'
1> or 'int [16]'
1>e:\dev_workspace\experimental\boost_msg_test\boost_msg_test\boost_msg_test.cpp(22)
: error C2782: 'void assign(T &,const T &)' : template parameter 'T' is
ambiguous
1>
e:\dev_workspace\experimental\boost_msg_test\boost_msg_test\boost_msg_test.cpp(10)
: see declaration of 'assign'
1> could be 'const int [16][16]'
1> or 'int [16][16]'


G++ compiles and calls the T[] version.
 
J

James Kanze

Someone in another list posted this code. It compiles in G++
and apparently is absorbed by comeau as well. VS vomits.
#include <cstddef>
template < typename T, std :: size_t sz >
struct Block
{
typedef T type[ sz ][ sz ];
};
template < typename T >
void assign( T & assignee, const T & value )
{}
template < typename T, std :: size_t sz >
void assign( T ( & assignee )[ sz ], const T ( & value )[ sz ] )
{}
int main( )
{
Block< int, 16 > :: type a;
const Block< int, 16 > :: type b = { };

assign( a, b );
}
1>e:\dev_workspace\experimental\boost_msg_test\boost_msg_test\boost_msg_test.cpp(22)
: error C2782: 'void assign(T (&)[sz],const T (&)[sz])' : template
parameter 'T' is ambiguous
1>
e:\dev_workspace\experimental\boost_msg_test\boost_msg_test\boost_msg_test.cpp(14)
: see declaration of 'assign'
1> could be 'const int [16]'
1> or 'int [16]'
1>e:\dev_workspace\experimental\boost_msg_test\boost_msg_test\boost_msg_test.cpp(22)
: error C2782: 'void assign(T &,const T &)' : template parameter 'T' is
ambiguous
1>
e:\dev_workspace\experimental\boost_msg_test\boost_msg_test\boost_msg_test.cpp(10)
: see declaration of 'assign'
1> could be 'const int [16][16]'
1> or 'int [16][16]'
G++ compiles and calls the T[] version.

It's a bug in VC++. Both function templates should be
instantiated (and the error message suggests they are) and
participate in overload resolution, but according to §14.5.5.2,
the second is clearly more specialized than the first, and
according to §13.3.3, if all other things are equal (and they
are, or at least they should be), the function instantiated from
the more specialized template is chosen.
 
A

Andrey Tarasevich

Noah said:
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'.
 
N

Noah Roberts

Andrey said:
Noah said:
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, value);
}


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[j] << " ";
std::cout << std::endl;
}

std::cin.get();
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,901
Latest member
Noble71S45

Latest Threads

Top