views:

210

answers:

1

The following code compiles in Visual C++ and gcc, but fails with Code Warrior

The complaint is that the call to the template is ambiguous -- can't decide between doIt( M* ) and doIt( M const* ), even though in each case, the parameter is unambiguously cost or non-const. Irritatingly, if I supply the second template argument, it decides it is no longer ambiguous.

template< typename T1, typename T2 >
T1 const* doIt( T2 const* );

template< typename T1, typename T2 >
T1* doIt( T2* );

class M {};
class N : public M {};

void f()
{
   M* m1 = NULL;
   M const* m2 = NULL;

   doIt<N>( m1 );    // Fail
   doIt<N>( m2 );    // Fail
   doIt<N,M>( m1 );  // OK
   doIt<N,M>( m2 );  // OK

}

Is this just an error with the Code Warrior compiler? (Or and error with gcc/Visual C++).

+5  A: 

It is an error with the codewarrior compiler.

This is what should happen:

template< typename T1, typename T2 >
T1 const* doIt( T2 const* );   // 1

template< typename T1, typename T2 >
T1* doIt( T2* );  // 2

class M {};
class N : public M {};

void f()
{
  M* m1 = 0;
  M const* m2 = 0;

  doIt<N>( m1 );    
  // In the above call - the compiler does the following (post argument deduction)
  //  1) create a viable set of functions  { N* doIt1<N,M>(const M*) , N* doIt2<N, M>(M*) }
  //  2) check the conversion sequences - M* -> M* is better than M* -> const M*
  //  Since doIt2 has a "better" conversion sequence (hard to beat identity) it wins - no ambiguity


  doIt<N>( m2 );    
  // 1) Viable functions: { doIt1<N,M>(const M*), doIt2<N,const M>(const M*) }
  // 2) Conversion Sequence Ranking: both do identity - so both are good
  // 3) Check to see if the "mother" template of either candidate is more specialized
  //     - Since doIt1 theoretically matches fewer types than doIt2, it is unambiguously more specialized (the standard specifies an algorithm to check this)
  //     - so doIt1 wins
}

Hope that helps.

Faisal Vali
+1, although for credits to warrior, the algorithm in c++03 in fact couldn't order them (try comeau 8/4/03 which fails to order too) - it would fail deducing "T1" in both directions. The revised algorithm at http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#214 can do it by ignoring "T1". I suspect that's what newer compilers use, since it's the intuitive thing anyway. But shame on warrior for rejecting the first call, of course.
Johannes Schaub - litb
@litb - good comment, it draws attention to the irrelevance of the return type when partially ordering these specific calls
Faisal Vali