C++ 重载 * 以乘以不同类型的分数

C++ overloading * to multiply fractions of different types

我有一个私有类型 N(分子)和 D(分母)的分数 class。这也允许类型是不同的数字类型。我还制作了一个覆盖星号的运算符函数,以便我可以执行 x*y,其中 x 和 y 代表小数对象。但是,除非两者都只使用一种类型,否则它不起作用。我怎样才能解决这个问题?为函数尝试了单独的模板,但看起来很复杂。此外,如果有一种方法可以在模板中仅表示数字类型(类似于 java 中的 Number class),将不胜感激:)

using namespace std;

template <typename N, typename  D> class Fraction {
private:
    N num;
    D denom;

public:
    Fraction(N numerator = 1, D denominator = 0) {
        assert(numerator != 0);
        this -> num   =     numerator;
        this -> denom =     denominator;
    }
    inline N getNumerator()    { return num;   }
    inline D getDenominator()  { return denom; }

    void setNumerator(N numerator)      { num = numerator;      }
    void setDenominator(D denominator)  { denom = denominator;  }

    friend Fraction operator*(Fraction& f, Fraction& g) {
        N numerator;
        D denominator;
        numerator = f.getNumerator() * g.getNumerator();
        denominator = f.getDenominator() * g.getDenominator();

        return Fraction(numerator, denominator);
    }

    friend Fraction operator*(const Fraction& f, int n) {
        return Fraction(n*f.getNumerator, f.getDenominator());
    }

    friend ostream& operator<<(ostream& os, const Fraction& f ) {
        return os << f.num << "/" << f.denom << endl;
    }
};

您的运营商必须有四个个模板参数:

template <typename N1, typename  D1, typename N2, typename  D2> 
Fraction<N1, D1> operator*(Fraction<N1, D1>& f, Fraction<N2, D2>& g) {
    N1 numerator;
    D1 denominator;
    numerator = f.getNumerator() * g.getNumerator();
    denominator = f.getDenominator() * g.getDenominator();

    return Fraction<N1, D1>(numerator, denominator);
}

(注意运算符不需要做成friend。)

template<class N, class D=N>
class Fraction {
    N numerator = 0;
    D denominator = 1;

    constexpr Fraction& operator*=(Fraction const& rhs)&{
      numerator *= rhs.numerator;
      denominator *= rhs.denominator;
      return *this;
    }
    constexpr Fraction& operator*=(int rhs)&{
      numerator *= rhs;
      return *this;
    }
    template<class N2, class D2>
    constexpr Fraction& operator*=(Fraction<N2, D2> const& rhs)&{
      numerator *= rhs.numerator;
      denominator *= rhs.denominator;
      return *this;
    }
    friend constexpr Fraction operator*(Fraction lhs, Fraction const& rhs) {
      lhs*=rhs;
      return std::move(lhs);
    }
    template<class N2, class D2>
    friend constexpr Fraction operator*(Fraction lhs, Fraction<N2,D2> const& rhs) {
      lhs*=rhs;
      return std::move(lhs);
    }

    friend constexpr Fraction operator*(Fraction lhs, int rhs) {
      lhs*=rhs;
      return std::move(lhs);
    }
    friend constexpr Fraction operator*(int lhs, Fraction rhs) {
      rhs*=lhs;
      return std::move(rhs);
    }

    friend ostream& operator<<(ostream& os, const Fraction& f ) {
        return os << f.numerator << "/" << f.denominator << endl;
    }
};
// deduction guide.  If you are mixing types, you have to pass in
// the type explicitly.  This is intentional.
template<class T>
Fraction(T, T)->Fraction<T,T>;

这是您尝试做的事情的一个不错的实现。

缺少访问器是有意的;这是毫无意义的样板。默认分数是 0/1 而不是 1/0.

非模板运算符很有用,因为它们允许

Fraction x{5,2};
x *= {2,3};
auto y = x*{1,2};

这种设计有点面向未来,是编写算术类型时可以遵循的一个很好的默认模式。它并不完美(它不支持对 a*b 的结果使用推导类型),但它解决了一堆你不知道的问题。

假设 ND 是某种 bigint(存储在堆上,但移动效率高);那么上面的构造将允许 * 的长链出现而无需重新分配;它将“免费”重复使用临时缓冲区。

Fraction<BigInt> a,b,c,d,e;
auto r = a*b*c*d*e;

a*b 复制 a,然后乘法,然后移动结果,然后 *c 不复制,然后 *d 不复制,然后执行*e 没有副本,然后将 a 的原始修改副本存储在 r

constexpr 垃圾邮件是因为在编译时做分数数学是一件合理的事情。