Topic: Why is this ambiguous?


Author: Terence Kelling <kelling@arlut.utexas.edu>
Date: 1998/08/10
Raw View
Why are the following template function call ambiguous for the line
shown?

#include <iostream>
using std::cout;
using std::endl;

template <class T>
class foo { };

template <class T, class U>
void func(T t,, const foo<U>& f) {

   cout << "func(const foo<U>&)" << endl;
}


template <class T, class U>
void func(const foo<T>& f, U u) {

   cout << "func(T, const foo<U>&)" << endl;
}

template <class T, class U>
void func(const foo<T>& f1, const foo<U>& f2) {

   cout << "func(const foo<T>&, const foo<U>&)" << endl;
}

void main(void) {

   foo<int> f;
   double d;

   func(d, f);
   func(f, d);
   func(f, f);    // <-ambiguous call
}

The first two calls work fine.  And the third should call the last
template function, but the compiler complains that it can not determine
which function to call.  The last is the most specific, both arguments
are of a type foo, why is it not chosen?

Thanks,
Terence Kelling



[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1998/08/11
Raw View
Terence Kelling wrote:
>
> Why are the following template function call ambiguous for the line
> shown?
>
> #include <iostream>
> using std::cout;
> using std::endl;
>
> template <class T>
> class foo { };
>
> template <class T, class U>
> void func(T t,, const foo<U>& f) {
>
>    cout << "func(const foo<U>&)" << endl;
> }
>
> template <class T, class U>
> void func(const foo<T>& f, U u) {
>
>    cout << "func(T, const foo<U>&)" << endl;
> }
>
> template <class T, class U>
> void func(const foo<T>& f1, const foo<U>& f2) {
>
>    cout << "func(const foo<T>&, const foo<U>&)" << endl;
> }
>
> void main(void) {
>
>    foo<int> f;
>    double d;
>
>    func(d, f);
>    func(f, d);
>    func(f, f);    // <-ambiguous call
> }
>
> The first two calls work fine.  And the third should call the last
> template function, but the compiler complains that it can not determine
> which function to call.  The last is the most specific, both arguments
> are of a type foo, why is it not chosen?

I think the last one should be chosen, and you have a bug in your
compiler. However, there's a workaround (which should continue to
work with a conforming compiler):

template<class T> class foo {};

template<class T> not_a_foo<T>
{
  typedef T type;
};

template<class T> not_a_foo< foo<T> >
{
  class type {};
};

template<class T, class U>
 void func(typename not_a_foo<T>::type t, const foo<U>& f)
{
  cout << "func(T, const foo<U>&)" << endl;
}

template <class T, class U>
 void func(const foo<T>& f, typename not_a_foo<U>::type u)
{
  cout << "func(const foo<T>&, U)" << endl;
}

template <class T, class U>
 void func(const foo<T>& f1, const foo<U>& f2)
{
  cout << "func(const foo<T>&, const foo<U>&)" << endl;
}

int main()
{
  foo<int> f;
  double d;

  func(d, f);
  func(f, d);
  func(f, f); // not any more ambiguous call
}

I haven't tried the code above, so there may be an error in it,
but the general mechanism works OK (I've already used it with
success).
The trick is that not_a_foo<T>::type is just T, *except*
if T is foo<anything>, in which case is is a class which is
guaranteed to be different from all other classes (since it's a
class local to not_a_foo< foo<anything> >); especially it is
*not* equal to foo<anything>. Therefore, the first two
functions are no match at all for func(f, f), and therefore
you won't get an ambuguous call.

In addition, it has the advantage that it indicates clearly
to the reader that the first two functions will not be used
if the first resp. second parameter is a foo (which otherwise
would not be obvious without looking at the other instantiations).
That is, it saves not only the compiler, but also the program
reader from analysing which template is the most specialized in
every specific case.

If your compiler doesn't like typename, just omit it (best done
by writing

#define typename

at the beginning; this way, if your compiler catches up, you
just have to remove that one line, instead of fixing all code).


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Wolfgang Bangerth <wolf@no-spam.gaia.iwr.uni-heidelberg.de>
Date: 1998/08/11
Raw View
Terence Kelling wrote:

> Why are the following template function call ambiguous for the line
> shown?
>
> template <class T, class U>
> void func(T t,, const foo<U>& f) {...}
>
> template <class T, class U>
> void func(const foo<T>& f, U u) {...}
>
> template <class T, class U>
> void func(const foo<T>& f1, const foo<U>& f2) {...}

>  ...

>    func(d, f);
>    func(f, d);
>    func(f, f);    // <-ambiguous call
>
> The first two calls work fine.  And the third should call the last
> template function, but the compiler complains that it can not determine
> which function to call.  The last is the most specific, both arguments
> are of a type foo, why is it not chosen?

No, the last one is not the most specified one, since the first one has
as first  parameter a value while the last template has a reference. Also,
they differ in the constness of their parameters. The last template
therefore is not more specialized than the first and both are
applicable to the third function call which makes the call ambiguous.

Regards
  Wolfgang




[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Terence Kelling <kelling@arlut.utexas.edu>
Date: 1998/08/11
Raw View
This is an extremely interesting solution, and I think it should work.
Unfortunately when I try to compile it using g++ 2.8.1, I get the following
errors. For each call to the template function "func" in main:

incomplete type unification.

Furthermore, the compiler states that there is no matching function for call
to func(double&, foo<int>&) or func(foo<int>&, double&).

Yet, intuitively, the process should work.  Am I missing something or is this
just pushing the current state of C++ compilers too far?

Thanks again for taking the time to help.

Terence Kelling

Christopher Eltschka wrote:

> I think the last one should be chosen, and you have a bug in your
> compiler. However, there's a workaround (which should continue to
> work with a conforming compiler):
>
> template<class T> class foo {};
>
> template<class T> not_a_foo<T>
> {
>   typedef T type;
> };
>
> template<class T> not_a_foo< foo<T> >
> {
>   class type {};
> };
>
> template<class T, class U>
>  void func(typename not_a_foo<T>::type t, const foo<U>& f)
> {
>   cout << "func(T, const foo<U>&)" << endl;
> }
>
> template <class T, class U>
>  void func(const foo<T>& f, typename not_a_foo<U>::type u)
> {
>   cout << "func(const foo<T>&, U)" << endl;
> }
>
> template <class T, class U>
>  void func(const foo<T>& f1, const foo<U>& f2)
> {
>   cout << "func(const foo<T>&, const foo<U>&)" << endl;
> }
>
> int main()
> {
>   foo<int> f;
>   double d;
>
>   func(d, f);
>   func(f, d);
>   func(f, f); // not any more ambiguous call
> }
>
> I haven't tried the code above, so there may be an error in it,
> but the general mechanism works OK (I've already used it with
> success).
> The trick is that not_a_foo<T>::type is just T, *except*
> if T is foo<anything>, in which case is is a class which is
> guaranteed to be different from all other classes (since it's a
> class local to not_a_foo< foo<anything> >); especially it is
> *not* equal to foo<anything>. Therefore, the first two
> functions are no match at all for func(f, f), and therefore
> you won't get an ambuguous call.
>
> In addition, it has the advantage that it indicates clearly
> to the reader that the first two functions will not be used
> if the first resp. second parameter is a foo (which otherwise
> would not be obvious without looking at the other instantiations).
> That is, it saves not only the compiler, but also the program
> reader from analysing which template is the most specialized in
> every specific case.
>
> If your compiler doesn't like typename, just omit it (best done
> by writing
>
> #define typename
>
> at the beginning; this way, if your compiler catches up, you
> just have to remove that one line, instead of fixing all code).



[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]