2013-03-29 3 views
1

Я изучаю перегруженные операторы на C++, и у меня есть проблема.Сбои с перегрузкой оператора или преобразования чисел

Я написал (примитивный) класс для представления доли в C++ и функцию для умножения двух из них.

Если я хочу умножить дробь на целое с помощью функции, все работает очень хорошо (благодаря конструктору преобразования: P). Но теперь я хочу умножить дроби на перегруженные *, точно так же, как два числа. Multiply first_fraction * second_fraction работает хорошо, но компилятор не хочет преобразовывать числа в дроби в fraction * 2. (Это Гав эту ошибку: ошибки C2666: «оператор *»: 2 перегруженных имеют аналогичные преобразования)

Если преобразовать его вручную, используя fraction*static_cast<CFraction>(2) он работает снова.

Может ли кто-нибудь объяснить мне, что я делаю плохо? Вот полный код:

#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. Я использую MS Visual C+++ 2010, если это дело

ответ

3

Проблема заключается в том, что ваш класс Fraction имеет конструктор, который не объявлен explicit и может принимать один аргумент типа int. Поэтому этот конструктор может быть выбран компилятором для реализации неявных пользовательских последовательностей преобразования каждый раз, когда требуется Fraction, но предоставляется int.

Кроме того, ваш тип Fraction также имеет оператор преобразования в float, что делает возможным неявно преобразовать Fraction в float каждый раз, когда float требуется, но Fraction предоставляется.

Таким образом, следующая инструкция неоднозначна:

CFraction result1 = fraction1*2; 

компилятор не знает, следует ли выбрать вашу перегрузку operator * для объектов типа Fraction и преобразовать второй аргумент (2) к Fraction с помощью конструктор Fraction передает 2 в качестве ввода или, скорее, преобразует первый аргумент в float через оператор преобразования, а затем использует встроенный operator * для выполнения умножения между float и int.

В C++ 11, вы можете решить, чтобы сделать оператор преобразования explicit, и это предотвратить бы неоднозначность: вы не сможете больше молча получить fraction1 превращали в 2, поэтому компилятор будет иметь только возможность чтобы преобразовать 2 в Fraction и вызвать перегрузку operator *.

В этом случае, если вы хотите, чтобы выполнить преобразование из Fraction в float, вы должны написать бросание явно:

float f = static_cast<float>(result1); 

Другой альтернативой является, чтобы сделать ваш конструктор explicit, а не оператора преобразования, так что компилятор не сможет мгновенно создать экземпляр Fraction, если предусмотрено одно значение, конвертируемое в целое.

Это позволит устранить двусмысленность для указанного выше умножения, поскольку он оставляет компилятор с единственным выбором преобразования fraction1 в float через ваш оператор преобразования. Однако возникнут две проблемы.

Во-первых, вы не могли бы больше писать:

CFraction result = multiplication(fraction1, 2); 

Поскольку это попытки создать Fraction из второго аргумента 2 (multiplication() ожидает два Fractions в качестве аргументов). Вместо этого вы должны создать объект явно:

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

Во-вторых, даже если ваше оригинальное умножение выше будет работать, окончательная копия инициализация result1 не будет, потому что (еще раз), что потребует неявное преобразования ,

Таким образом, вам придется переписать копию инициализацию как:

CFraction result1 = CFraction(fraction1*2); 

Или как прямой инициализации:

CFraction result1(fraction1*2); 
+0

Благодарим вас за исчерпывающий ответ –

+0

@ArkadiuszRosiak: Добро пожаловать, рад, что это помогло :) –

0
CFraction result2 = fraction1 * 2; 

Как использовать результат не оказывает никакого влияния на то, как он вычисляется, поэтому это справедливо:

fraction1 * 2 

Это неоднозначно. Вы можете конвертировать fraction1 в поплавок и умножить это на 2. Вы можете конвертировать 2 в Fraction, а затем использовать operator*. Компилятор понятия не имеет, чего вы хотите.

0

Поскольку вы поставляете преобразование плавать в классе CFraction, компилятор имеет выбор преобразования

CFraction result1 = fraction1*2; 

использовать либо свой собственный оператор * с CFractions, или стандартный оператор * с поплавком и внутр.

Если вы можете использовать C++ 11, вы можете отметить свое преобразование с плавающей точкой с явным. В противном случае переименуйте его в нечто вроде «as_float». Неявные преобразования часто проблематичны и их лучше избегать.

0

Попробуйте использовать шаблоны и левые преобразования для компилятора.

Смежные вопросы