简体   繁体   中英

Troubles with overloading operator or numbers conversion

I'm learning about overloaded operators in C++, and i've got a problem.

I wrote a (primitive) class to represent a fraction in c++, and a function to multiply two of them.

If I want to multiply fraction by integer using function, everything works very well (thanks to conversion constructor :P). But now i want to multiply fractions by overloaded *, just like two numbers. Multiply first_fraction * second_fraction works good, but compilator don't want to convert numbers to fraction in fraction * 2 . (It gaves this error: error C2666: 'operator *' : 2 overloads have similar conversions)

If I convert it manually, using fraction*static_cast<CFraction>(2) it works again.

Can anyone explain me what am I doing bad? Here's the full code:

#include <iostream>

using namespace std;

class CFraction
{

private:
    int m_numerator;
    int m_denominator;

public:

    // Normal constructor, default constructor and conversion constructor
    CFraction(int numerator=0,int denominator = 1) : m_numerator(numerator), m_denominator(denominator)
    {               
    }

    int numerator() const { return m_numerator; }
    void numerator(int numerator) { m_numerator = numerator; }

    int denominator() const { return m_denominator; }
    void denominator(int denominator) { m_denominator = denominator; }

    // Conversion to decimal form
    operator float()
    {
        return m_numerator / static_cast<float>(m_denominator);
    }   

};

// Function to multiply 2 fractions
CFraction multiplication(const CFraction& f1,const CFraction& f2)
{
    return CFraction(f1.numerator()*f2.numerator(),f1.denominator()*f2.denominator());
}

// Overloaded opearator to multiply 2 fractions
CFraction operator *(const CFraction& f1,const CFraction& f2)
{
    return CFraction(f1.numerator()*f2.numerator(),f1.denominator()*f2.denominator());
}

int main()
{
    CFraction fraction1(3,4);

    cout << "Fraction: "<< fraction1.numerator() << "/" << fraction1.denominator() << endl;
    cout << "Decimal: " << static_cast<float>(fraction1) << endl;

    // Multiplication by function works very well
    CFraction result = multiplication(fraction1,2);

    // (ERROR) Compiller won't convert 2 to CFraction class
    CFraction result1 = fraction1*2;

    // Using manually covnerted integer - works again
    CFraction result2 = fraction1*static_cast<CFraction>(2);

    cout << "Multiplied by 2: " << static_cast<float>(result);

    getchar();
    return 0;
}

PS. I'm using MS Visual C+++ 2010 if it's matter

The problem is that your class Fraction has a constructor which is not declared as explicit and can accept one argument of type int . Therefore, this constructor can be picked by the compiler to realize implicit user-defined conversion sequences every time a Fraction is needed, but an int is provided.

Moreover, your type Fraction also has a conversion operator to float , which makes it possible to implicitly convert Fraction to float every time a float is required, but a Fraction is provided.

Therefore, the following instruction is ambiguous:

CFraction result1 = fraction1*2;

The compiler doesn't know whether to pick your overload of operator * for objects of type Fraction and convert the second argument ( 2 ) to a Fraction by using the constructor of Fraction an passing 2 as an input, or rather convert the first argument to a float through the conversion operator and then use the built-in operator * to perform the multiplication between a float and an int .

In C++11, you could decide to make the conversion operator explicit , and this would prevent the ambiguity: you won't be able anymore to silently get fraction1 converted to 2 , so the compiler will have only the option to convert 2 into a Fraction and invoke your overload of operator * .

In this case, if you want to perform the conversion from Fraction to float , you have to write the cast explicitly:

float f = static_cast<float>(result1);

The other alternative is to make your constructor explicit rather than the conversion operator, so that the compiler won't be able to silently instantiate a Fraction when a single value convertible to an integer is provided.

This will solve the ambiguity for the above multiplication itself, in that it leaves the compiler with the only choice of converting fraction1 into a float through your conversion operator. However, two problems will arise.

First, you would not be able anymore to write:

CFraction result = multiplication(fraction1, 2);

Because this attempts to create a Fraction out of the second argument 2 ( multiplication() expects two Fractions as its arguments). Instead, you would have to construct the object explicitly:

CFraction result = multiplication(fraction1, CFraction(2));

Second, even though your original multiplication above would work, the final copy-initialization of result1 would not, because (once again) that would require an implicit conversion.

Therefore, you would have to rewrite your copy-initialization as:

CFraction result1 = CFraction(fraction1*2);

Or as a direct initialization:

CFraction result1(fraction1*2);
    CFraction result2 = fraction1 * 2;

How you use a result has no effect on how it's computed, so this is just:

   fraction1 * 2

This is ambiguous. You could convert fraction1 to a float and multiply that by 2. You could convert 2 to a Fraction and then use operator* . The compiler has no idea which you want.

Because you supply a conversion to float in your CFraction class, the compiler has a choice of converting

CFraction result1 = fraction1*2;

to use either your own operator * with CFractions, or the standard operator * with a float and an int.

If you can use C++11, you could mark your float conversion with explicit. Otherwise, rename it to something like "as_float". Implicit conversions are often problematic and are best avoided.

尝试使用模板并将转换留给编译器。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM