Template considered ambiguous

N

neildferguson

I am using templates with a little project I am working on. My compiler (GCC)
is finding a particular construct ambiguous. Can anyone suggest something I
might change in the declaration of class Length so that I can use operator+ the
way I'd like?

//=========================================
// File lentest.h:

#ifndef FDIMENS_LENGTH_INCL
#define FDIMENS_LENGTH_INCL

class EU {};
class MT : public EU {};
class KM : public EU {};
class FT : public EU {};
class YD : public EU {};

template <class U>
class Length
{
public :
Length<U>()
{
}
Length<U>(double inUnits)
{
}
template <class O>
Length<U>(const Length<O> &other)
{
}
template <class O>
Length<U> &operator=(const Length<O> &other)
{
return *this;
}
template <class O>
friend Length<U> operator+(const Length<U> &lhs, const Length<O>
&rhs)
{
Length<U> ret;
return ret;
}
template <class O>
friend Length<U> operator+(double lhs, const Length<O> &rhs)
{
Length<U> ret;
return ret;
}
operator double() const { return m_lenUnits; }
private :
static double m_m2u;
static double m_u2m;
};
template<> double Length<MT>::m_m2u = 1.0;
template<> double Length<MT>::m_u2m = 1.0;

template<> double Length<KM>::m_m2u = 0.001;
template<> double Length<KM>::m_u2m = 1000.0;

template<> double Length<FT>::m_m2u = 3.28083986538;
template<> double Length<FT>::m_u2m = 0.3048;

template<> double Length<YD>::m_m2u = (3.28083986538/3.0);
template<> double Length<YD>::m_u2m = (0.3048*3.0);
#endif // FDIMENS_LENGTH_INCL

//=============================================
// File lentest.cpp
// lentest.cpp : Defines the entry point for the console application.
//
#include <stdio.h>

#include "lentest.h"

int main()
{
Length<FT> ft1(34.0);
Length<FT> ft2 = (Length<FT>)27.0 + ft1;
#if 0 // this line is ambiguous
Length<FT> ft3 = 27.0 + ft2;
#endif
return 0;
}
 
V

Victor Bazarov

I am using templates with a little project I am working on. My
compiler (GCC)
is finding a particular construct ambiguous. Can anyone suggest
something I
might change in the declaration of class Length so that I can use
operator+ the way I'd like?

Change compilers? Both Comeau online and VC++ 2005 are fine with
your code...

V
 
N

neildferguson

I am using templates with a little project I am working on. My
compiler (GCC)
is finding a particular construct ambiguous. Can anyone suggest
something I
might change in the declaration of class Length so that I can use
operator+ the way I'd like?

Change compilers? Both Comeau online and VC++ 2005 are fine with
your code...

V
You mean I did it correctly? That's the last thing I would have guessed.

Unfortunately my MSDN set is rather old - VisualC++ expires from internal
injuries. I tried upgrading (Cygwin) gcc to version 3.4.4, without any better
luck. Time to rethink.

And thank you for the referral to Comeau online.

Neil

BTW: The error is:

lentest.cpp:12: error: ambiguous overload for 'operator+' in '2.7e+1 + ft2'
lentest.cpp:12: note: candidates are: operator+(double, double) <built-in>
lentest.h:31: note: Length<U> operator+(const Length<U>&, const Length<O>&)
[with O = FT, U = MT]
lentest.h:37: note: Length<U> operator+(double, const Length<O>&) [with O = FT,
U = MT]
lentest.h:31: note: Length<U> operator+(const Length<U>&, const Length<O>&)
[with O = FT, U = KM]
lentest.h:37: note: Length<U> operator+(double, const Length<O>&) [with O = FT,
U = KM]
lentest.h:31: note: Length<U> operator+(const Length<U>&, const Length<O>&)
[with O = FT, U = FT]
lentest.h:37: note: Length<U> operator+(double, const Length<O>&) [with O = FT,
U = FT]
lentest.h:31: note: Length<U> operator+(const Length<U>&, const Length<O>&)
[with O = FT, U = YD]
lentest.h:37: note: Length<U> operator+(double, const Length<O>&) [with O = FT,
U = YD]
 
K

Kai-Uwe Bux

Victor said:
Change compilers? Both Comeau online and VC++ 2005 are fine with
your code...

Hm, maybe they are both wrong.


Consider

template <class U> class Length {
public :
// ...
template <class O>
friend Length<U> operator+(double lhs, const Length<O> &rhs)
{ ... }
// ...
};

Then, in main we find:

27.0 + ft2

where ft2 is of type Length<FT>. How is the compiler supposed to deduce
whether the result will be of type Length<FT> or Length<YD> or ...? Each
Length<T> wants to add an overload that matches the arguments perfectly.

Am I missing some rule on overload resolution that would disambiguate?


Best

Kai-Uwe Bux
 
I

Ian Collins

Kai-Uwe Bux said:
Victor Bazarov wrote:

Hm, maybe they are both wrong.

Consider

template <class U> class Length {
public :
// ...
template <class O>
friend Length<U> operator+(double lhs, const Length<O> &rhs)
{ ... }
// ...
};

Then, in main we find:

27.0 + ft2

where ft2 is of type Length<FT>. How is the compiler supposed to deduce
whether the result will be of type Length<FT> or Length<YD> or ...? Each
Length<T> wants to add an overload that matches the arguments perfectly.

Am I missing some rule on overload resolution that would disambiguate?
I don't think it can, hence the error form gcc, which lists all the
possibilities!

Sun CC gives what on the surface appears an unhelpful error:

Overloading ambiguity between "operator+<FT>(double, const Length<FT>&)"
and "operator+<FT>(double, const Length<FT>&)"

Which is makes sense given the ambiguity is with the return type.
 
J

James Kanze

Hm, maybe they are both wrong.

Or maybe g++ is still supporting some older rules of name
injection (perhaps intentionally, to avoid breaking existing
code.
template <class U> class Length {
public :
// ...
template <class O>
friend Length<U> operator+(double lhs, const Length<O> &rhs)
{ ... }
// ...

Note that according to the standard, this operator is *NOT*
visible in global namespace; the name of friend functions is not
injected into the enclosing namespace. That means that this
function can only be found 1) if the code is in a member
function, or 2) if ADL brings it into scope.
Then, in main we find:
27.0 + ft2
where ft2 is of type Length<FT>. How is the compiler supposed
to deduce whether the result will be of type Length<FT> or
Length<YD> or ...?

Why does it have to deduce? Normal lookup finds none of the
and only that said:
Each Length<T> wants to add an overload that matches the
arguments perfectly.
Am I missing some rule on overload resolution that would
disambiguate?

The fact that the operator+ funnction is only visible because of
ADL. And ADL only looks in Length<FT>.
 
K

Kai-Uwe Bux

James said:
Or maybe g++ is still supporting some older rules of name
injection (perhaps intentionally, to avoid breaking existing
code.

I doubt that it is intentional. My experience with g++ is that upgrades can
be pretty reckless.

Note that according to the standard, this operator is *NOT*
visible in global namespace; the name of friend functions is not
injected into the enclosing namespace. That means that this
function can only be found 1) if the code is in a member
function, or 2) if ADL brings it into scope.





Why does it have to deduce? Normal lookup finds none of the
overloads. ADL finds the overload in Length<FT>, and only that
overload.

Good point.

That implies, however, that

Length<U>::eek:perator+( double, Length<O> const & )

will only be found through ADL when U=O because only Length<O> will be
searched.

In that case, the following should suffice:

template <class U> class Length {
public :
// ...
friend Length operator+(double lhs, const Length &rhs)
{ ... }
// ...
};


Maybe this rewrite will placate g++.



Best

Kai-Uwe
 
J

James Kanze

I doubt that it is intentional. My experience with g++ is that
upgrades can be pretty reckless.

But usually in the opposite direction. Working code breaks,
because it isn't 100% standards conform. (The fact that the
code was written before the standard doesn't seem to bother the
g++ maintainers. And to be fair, it depends on which
one---sometimes, they do a very good job with regards to
backwards compatibility, and other times, you get the impression
that they went out of their way to break it.)
Good point.
That implies, however, that
Length<U>::eek:perator+( double, Length<O> const & )
will only be found through ADL when U=O because only Length<O>
will be searched.

Pretty much, I think:).

Off hand, my impression was that the OP had gotten a little bit
carried away with templated operators, to the point of possibly
looking for trouble (ambiguous calls).

In this case, if the compiler implements the older rules (with
friends being injected into the surrounding namespace), the call
can only be ambiguous, since the compiler really has to deduce
two types, and it doesn't have the information for one of them.
If the compiler implements the new rules (with no friend name
injection, but with ADL finding the friend), then the only time
the function will be found is when O and U are the same.

Note that something like:

template< typename O >
friend Length operator+( Length const& lhs, Length<O> const&
rhs ) ;

would work. The version of operator+ that would be used would
always come from the type of the first operand, of course.
In that case, the following should suffice:
template <class U> class Length {
public :
// ...
friend Length operator+(double lhs, const Length &rhs)
{ ... }
// ...
};
Maybe this rewrite will placate g++.

Probably. I use something fairly similar all the time:

--------- Operators.hh ----------
template< typename Owner >
class Operators
{
friend Owner operator+( Owner const& lhs, Owner const& rhs )
{
Owner result( lhs ) ;
result += rhs ;
return result ;
}
// ...
} ;

---------- SomeOtherClass.hh ---------
class Toto : public Operators< Toto >
{
} ;

Of course, Operators implements *all* of the usual operators,
along the same lines. So that anytime I implement <op>= as a
member function, by deriving from Operator, I get the
corresponding binary function.
 
K

Kai-Uwe Bux

James said:
But usually in the opposite direction. Working code breaks,
because it isn't 100% standards conform.

I think, that _is_ intentional.

The reason I think the current case is not intentional is precisely that it
is the other way around: the compiler chokes on what appears to be correct
code.


[snip]


Best

Kai-Uwe Bux
 
N

neildferguson

I doubt that it is intentional. My experience with g++ is that upgrades can
be pretty reckless.



Good point.

That implies, however, that

Length<U>::eek:perator+( double, Length<O> const & )

will only be found through ADL when U=O because only Length<O> will be
searched.

In that case, the following should suffice:

template <class U> class Length {
public :
// ...
friend Length operator+(double lhs, const Length &rhs)
{ ... }
// ...
};


Maybe this rewrite will placate g++.



Best

Kai-Uwe
It seems to have done the trick! I apparently need similar routines for int &
unsigned int (which handle char, unsigned char, short, unsigned short), plus
float.

Many thanks.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top