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
的结果使用推导类型),但它解决了一堆你不知道的问题。
假设 N
或 D
是某种 bigint(存储在堆上,但移动效率高);那么上面的构造将允许 *
的长链出现而无需重新分配;它将“免费”重复使用临时缓冲区。
Fraction<BigInt> a,b,c,d,e;
auto r = a*b*c*d*e;
a*b
复制 a
,然后乘法,然后移动结果,然后 *c
不复制,然后 *d
不复制,然后执行*e
没有副本,然后将 a
的原始修改副本存储在 r
。
constexpr
垃圾邮件是因为在编译时做分数数学是一件合理的事情。
我有一个私有类型 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
的结果使用推导类型),但它解决了一堆你不知道的问题。
假设 N
或 D
是某种 bigint(存储在堆上,但移动效率高);那么上面的构造将允许 *
的长链出现而无需重新分配;它将“免费”重复使用临时缓冲区。
Fraction<BigInt> a,b,c,d,e;
auto r = a*b*c*d*e;
a*b
复制 a
,然后乘法,然后移动结果,然后 *c
不复制,然后 *d
不复制,然后执行*e
没有副本,然后将 a
的原始修改副本存储在 r
。
constexpr
垃圾邮件是因为在编译时做分数数学是一件合理的事情。