Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Instantiation of a template function

1 view
Skip to first unread message

Vladimir Grigoriev

unread,
Dec 21, 2009, 9:15:16 AM12/21/09
to

Let consider the code

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 Johnston

unread,
Dec 21, 2009, 10:08:18 AM12/21/09
to
Any C++ compiler will give a similar error, rules for template argument
deduction are different than type conversions for non-template functions I
guess.

/Leigh

Igor Tandetnik

unread,
Dec 21, 2009, 12:36:58 PM12/21/09
to
Vladimir Grigoriev <vlad....@mail.ru> wrote:
> Let consider the code
>
> 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

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

Vladimir Grigoriev

unread,
Dec 21, 2009, 12:55:56 PM12/21/09
to
Thanks, Igor.
However when a non-template function is used the compiler performs all this
conversion. 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.

Vladimir Grigoriev


"Igor Tandetnik" <itand...@mvps.org> wrote in message
news:OE4uzQmg...@TK2MSFTNGP02.phx.gbl...

Igor Tandetnik

unread,
Dec 21, 2009, 1:30:04 PM12/21/09
to
Vladimir Grigoriev <vlad....@mail.ru> wrote:
> However when a non-template function is used the compiler performs
> all this conversion.

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.

0 new messages