tags:

views:

727

answers:

3

Hello all,

Am trying to move an antique C++ code base from gcc 3.2 to gcc 4.1, as I have run into a few issues. Of all the issues, the following is left me clueless (I guess I spent too much time with Java or I might have forgotten the basics of C++ :-P ).

I have a template class

template < class T > class XVector < T >
{
   ...
   template < class T > T
   XVector < T >::getIncrement ()
   {
       ...
   }  
   template < class T > int
   XVector < T >::getValue (size_t index, T& value)
   {
      ...
      //The problematic line
      value = (T) (first_value + getIncrement())
                  * (long) (index - first_index);
      ....
   }
}

This class is based on the STL std::vector. I have a second class TypeValue and its defined as below which can hold one of int, long and their unsigned variants, float, double, std::string. Also overloads almost all possible operators.

class TypeValue
{
    union
    {
        long* _int_value;
        double* _real_value;
        string* _text_value;
    } _value;

    TypeValue();
    explicit TypeValue(long _long);
    explicit TypeValue(int _int);
    explicit TypeValue(unsigned long _ulong);
    ...
    //similarly for all the remaining supported types.
    TypeValue(const TypeValue& ) //Copy constructor

    virtual TypeValue& operator=(const TypeValue &rhs);
    TypeValue& operator+ (TypeValue& )const;
    TypeValue& operator* (TypeValue& )const;
    ...
    //For all the basic operators

    operator long() const;
    operator int() const;
    operator unsigned long() const;
    operator unsigned int() const;
    ...

}

And finally I have another class, lets call it the build_breaker, which creates an object as XVector < TypeValue > a_variable;. Now when I compile this on gcc 3.2 this compiles without any problems. But when I try compiling this on gcc 4.1 I get errors saying ambigous overload for operator* in the class XVector and the candidates being

operator*(long int, long int) 
operator*(int, long int) 
operator*(long unsigned int, long int) 
operator*(unsigned int, long int) 
operator*(double, long int) 
operator*(float, long int)  

If the compiler said it had problems finding a match for T * long, that would have made sense, but, why is it trying to typecast it to native type and then perform the arithmetic operation? Please help me on this.

Thanks in advance.

A: 

We'd need to know what is T. It looks like you are instancing an XVector using some type (like unsigned char, for instance) that can be converted into all those types you see, and the compiler does not know which one to choose.

Gorpik
T is TypeValue.
xtofl
In this case, it is quite clear. There are lots of implicit conversions from TypeValue and the compiler does not know which one to choose. In fact, I don't understand how could this ever compile under gcc 3.2
Gorpik
This code compiles without errors or warnings in gcc 3.2.3, which still amuses me!
cx0der
+4  A: 

The second operand type is long [int]. The first is TypeValue, I expect, but there is no operator* that takes those two exact types. There are lots of other type combinations for that operator, though, which the compiler can select by doing an implicit conversion on the first operand. The language allows the compiler to do that to try to find a match.

But which of the many conversions should it choose? The compiler has no way to choose whether int is better than long int. (You might argue that since the second operand is long int, that should be the preferred conversion target, but that's not the way things work.)

So, some advice: First, don't supply so many implicit conversions. Since the class can only hold long, double, and string, those are the only three conversions I'd supply. That alone probably won't solve your problem, but it may reduce the size of the error output and make other things more manageable.

Instead of converting (index - first_index) to type long, consider converting it to type T (i.e., TypeValue) instead, since that seems to be the operation you really wanted to perform in the first place.

Rob Kennedy
cx0der
I didn't advise providing a TypeValue-long overload for operator*, did I? Provide FEWER conversions and FEWER overloads. The only operator* you should provide is this: <<TypeValue operator*(TypeValue const>> It's a const method that accepts a const reference and returns a value. (Don't return a reference.) Then, like I said before, type-cast (index - first_index) to be type T, at which point the compiler will construct a TypeValue object from that difference and multiply it with the first TypeValue object you have.
Rob Kennedy
+2  A: 

I would change all the conversion operators to named functions so that:

operator long() const;

becomes

long ToLong() const;

Implicit conversiopns via cast operators cause all sorts of problems, and my own programming standards bans their use.

anon