Discussion:
Instantiation of a template function
(too old to reply)
Vladimir Grigoriev
2009-12-21 14:15:16 UTC
Permalink
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
2009-12-21 15:08:18 UTC
Permalink
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
2009-12-21 17:36:58 UTC
Permalink
Post by Vladimir Grigoriev
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.
Post by Vladimir Grigoriev
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
2009-12-21 17:55:56 UTC
Permalink
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
Post by Vladimir Grigoriev
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.
Post by Vladimir Grigoriev
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
Igor Tandetnik
2009-12-21 18:30:04 UTC
Permalink
Post by Vladimir Grigoriev
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.
Post by Vladimir Grigoriev
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.
--
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
Loading...