提问者:小点点

如何解决不明确的运算符重载问题?


我有一个类fraction定义为:

class Fraction
{
  int d_numerator;
  int d_denominator;

  Fraction(numerator, denominator)
    :
  d_numerator(numerator), d_denominator(denominator)
  {}

  Fraction(double number)
    :
  d_numerator(static_cast<int>(number * 100000)), d_denominator(100000)
  {}
};

我需要重载运算符+以允许添加2个分数对象:

Fraction Fraction::operator+(const Fraction& other)
{
  Fraction result = Add(other); //Add() implementation omitted for brevity
  return result;
}

我有一个重载的运算符(double)函数,它允许我将fraction对象强制转换为double类型,例如double b=(double)a;其中afraction类型。

Fraction::operator(double){//omitted for brevity}

我希望能够向分数添加一个double,如下所示:分数c=a+2.6;

问题是这将无法编译,因为有几种方法解释语句fraction c=a+2.6;的转换

我得到以下编译器错误:

错误C2666:“Fraction::Operator+”:3重载具有类似的转换

  1. “Fraction Fraction::operator+(常量分数&)
  2. 或内置C++运算符+(double,double)
  3. 或内置C++运算符+(浮点,双值)

我认为这其中的实质是,编译器是否应该:

  1. 使用运算符(double)重载将分数a转换为双倍,然后将两个双倍相加
  2. 或者,使用允许这样做的构造函数将double 2.6转换为分数,然后添加两个分数

有没有一种方法可以强制编译器使用另一条路由中的一条,或者让编译器选择任何一条。 只要结果如所料,我不介意。

谢谢


共2个答案

匿名用户

其中一个问题是在将adding运算符声明为成员函数时缺少常量

如果方法不是常量,那么当使用运算符+时,大多数编译器都会抱怨。

class Fraction
{
public:
  Fraction(int numerator, int denominator);
  Fraction(int value); // needed to prevent unplanned use of Fraction(double number);
  Fraction(double number);

  // rule of zero!

  Fraction added(const Fraction& b) const; // this returns result of adding current object with argument
  void add(const Fraction& b); // NO const! this adds value to current object

private:
  int d_numerator;
  int d_denominator;
};

Fraction operator+(const Fraction& a, const Fraction& b)
{
    return a.added(b);
}

godbolt或cppinsights

匿名用户

我制作了一个工作示例,用于在代码中添加两个带有注释的分数。 用其他操作符扩展它是一个练习。

我还使用了C++17函数std::gcd,因为在使用这些函数时,数字越小越好。

#include <iostream>
#include <numeric>  // std::gcd

class Fraction {
public:
    Fraction(int numerator, int denominator) :
        d_numerator(numerator), d_denominator(denominator)
    {
        inorm();
    }

    // a converting constructor
    explicit Fraction(double number) :
        d_numerator(static_cast<int>(number * 1000000)), d_denominator(1000000)
    {
        norm();
    }

    // add a Fraction to *this 
    Fraction& operator+=(const Fraction& o) {
        d_numerator = d_numerator* o.d_denominator + o.d_numerator * d_denominator;
        d_denominator *= o.d_denominator;
        norm();
        return *this;
    }

    // implicit conversion to double
    operator double () const {
        return static_cast<double>(d_numerator) / d_denominator;
    }

    // for printing a Fraction
    friend std::ostream& operator<<(std::ostream&, const Fraction&);

private:
    int d_numerator;
    int d_denominator;

    void norm() {
        // find the greatest common divisor and apply it
        const auto gcd = std::gcd(d_numerator, d_denominator);        
        d_numerator /= gcd;
        d_denominator /= gcd;
    }

    void inorm() {
        if(d_numerator == 0) d_denominator = 1;
        else if(d_denominator == 0) throw std::runtime_error("denominator==0");
        norm();
    }
};

std::ostream& operator<<(std::ostream& os, const Fraction& f) {
    return os << '(' << f.d_numerator << '/' << f.d_denominator << ')';
}

// A free function to add two Fractions - take the left hand side by value
// and you can return the same object (after having added the right hand side to it).
Fraction operator+(Fraction a, const Fraction& b) {
    // use the member operator+=
    return a += b;
}


int main() {
    Fraction a(1, 2);
    Fraction b(0.748624);   // use the converting constructor

    auto c = a + b;         // use the free function
    std::cout << c << '\n'; // (78039/62500)
    double d = c;           // use implicit conversion to double
    std::cout << d;         // 1.24862
}