struct A
{
};
struct B: public A
{
}
template <typename T>
void f( const T &, const T & )
{
}
int _tmain(int argc, _TCHAR* argv[])
{
f( A(), B() );
}
The Microsoft C++ compiler generated the error
error C2782: 'void f(const T &,const T &)' : template parameter 'T' is
ambiguous
see declaration of 'f'
could be 'B'
or 'A'
However when I substitute the template function to usual function
void f( const A &, const A & )
{
}
The code is compiled.
Should the compiler take into account that there is only one available
conversion from B object ti A object when it instantiates the template
function? Why does it report the error though B type can not be used as T?
Vladimir Grigoriev
/Leigh
Template parameter deduction is performed separately for each function argument, then results are combined. It's an error if they contradict each other.
In your example, decuction on the first argument results in T==A, deduction on the second results in T==B. Hence the error.
> Should the compiler take into account that there is only one available
> conversion from B object ti A object when it instantiates the template
> function?
You want the compiler to solve a very difficult problem when performing template parameter deduction. You want it to consider every possible implicit conversion sequence that the actual parameter may undergo, and then try and choose a sequence for each type that would lead to an unambiguous and non-contradictory assignment of actual types to template parameters. While the behavior may seem obvious in your particular case, it will become non-obvious in general.
Say, should user-defined conversions be taken into account? What if B weren't derived from A, but provided operator const A&(); or else, A had a constructor from const B& ? What if B provided a templated conversion operator that could match const A&, or A provided a templated constructor that could match const B&? Then you'd piling one template parameter deduction on top of another.
What if A and B were unrelated, but there was a third type C providing constructors from both const A& and const B&? Would you expect f( A(), B() ) call to be interpreted as f( C(A()), C(B()) ) ? If you answer "yes", imagine there were two such classes, C and D: now the meaning of f( A(), B() ) depends on whether the declarations of C, D, both or neither happen to be visible at that particular point in the program.
To avoid all this mess, template parameter deduction is defined to be simple and predictable. If you don't like the outcome, you can always specify parameters explicitly, as in
f<A>( A(), B() );
--
With best wishes,
Igor Tandetnik
With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
.1.a. convert template to non-template
f<A>( A(), B() );
1.b.. convert template to non-template
f<B>( A(), B() );
2.a If only one variant of non-template function is valid use it. Otherwise
report ambigious using.
Vladimir Grigoriev
"Igor Tandetnik" <itand...@mvps.org> wrote in message
news:OE4uzQmg...@TK2MSFTNGP02.phx.gbl...
It doesn't need to perform template parameter deduction. If you specify template parameters explicitly, then the necessary conversions will be performed for a template function, too.
> Si it is not clear whay it can not consider
> two-step deduction.
>
> .1.a. convert template to non-template
> f<A>( A(), B() );
> 1.b.. convert template to non-template
> f<B>( A(), B() );
> 2.a If only one variant of non-template function is valid use it.
> Otherwise report ambigious using.
There are two problems with that. First, while it helps in your particular case, it's still not very intuitive. Consider this case, only slightly different:
class Base {};
class A: public Base {};
class B: public Base {};
Now, neither f<A>( A(), B() ) nor f<B>( A(), B() ) compiles, though f<Base>( A(), B() ) does. So, why make the logic more complicated only to help one narrow situation?
Second, this approach may cause the compiler to perform exponential amount of work. Consider:
template <typename T1, typename T2, ..., typename Tn>
void f(const T1&, const T1&, const T2&, const T2&, ..., const Tn&, const Tn&);
f(A(), B(), A(), B(), ..., A(), B());
It appears your approach would force the compiler to perform 2^n instantiations of f: f<A, A, ..., A>, f<A, A, ..., B>, ..., f<B, B, ..., B>. Again, for little apparent benefit.