定义一个合适的减法运算符

时间:2009-06-27 09:10:41

标签: c++ operator-overloading

我为数学对象编写了一个抽象类,并定义了所有运算符。使用它时,我遇到了:

Fixed f1 = 5.0f - f3;

我只定义了两个减法运算符:

inline const Fixed operator - () const;
inline const Fixed operator - (float f) const;

我在这里得到了什么错误 - 加法是可交换的(1 + 2 == 2 + 1)而减法不是(乘法和除法相同)。 我立即在我的课外写了一个函数,如下所示:

static inline const Fixed operator - (float f, const Fixed &fp);

但后来我意识到这不可能做到,因为为了做到这一点,我将不得不触及类的私有,这导致使用我厌恶的关键字friend,以及用'静态污染命名空间'不必要的功能。

在类定义中移动函数会在gcc-4.3中产生此错误:

error: ‘static const Fixed Fixed::operator-(float, const Fixed&)’ must be either a non-static member function or a non-member function

按照GCC建议,并使其成为非静态函数会导致以下错误:

error: ‘const Fixed Fixed::operator-(float, const Fixed&)’ must take either zero or one argument

为什么我不能在类定义中定义相同的运算符?如果无法做到这一点,那么其他方面是否还没有使用friend关键字?

同样的问题适用于分裂,因为它遇到了同样的问题。

4 个答案:

答案 0 :(得分:3)

如果您需要放心,朋友的功能可以正常:

http://www.gotw.ca/gotw/084.htm

  

需要访问哪些操作   否则我们会有内部数据   通过友谊授予?这些应该   通常是会员。 (有一些   罕见的例外,例如操作   需要左手转换   参数和一些像运算符<<()   其签名不允许* this   参考是他们的第一个   参数;即使这些通常也可以   非友好的实施方式   (可能是虚拟的)成员,但是   有时这样做只是一个   在扭曲中锻炼,他们是   最好的,自然地表达为   朋友。)

您正处于“需要在左侧参数上进行转换的操作”阵营中。如果您不想要朋友,并且假设您有float的非显式Fixed构造函数,则可以将其实现为:

static inline Fixed operator-(const Fixed &lhs, const Fixed &rhs) {
    return lhs.minus(rhs);
}

然后将minus实现为公共成员函数,大多数用户都不会因为喜欢运营商而烦恼。

我假设你有operator-(float),那么你有operator+(float),所以如果你没有转换运营商,你可以选择:

static inline Fixed operator-(float lhs, const Fixed &rhs) {
    return (-rhs) + lhs;
    // return (-rhs) -(-lhs); if no operator+...
}

如果您有明确的Fixed(lhs) - rhs构造函数,则只需float。那些可能会或可能不会像您的朋友实施那样有效。

不幸的是,语言不会向后倾斜,以适应那些碰巧厌恶某个关键字的人,因此操作符不能成为静态成员函数并以这种方式获得友谊的效果;-p

答案 1 :(得分:1)

  1. “这就是朋友的意思......”
  2. 您可以在float和您的类型之间添加隐式转换(例如,使用构造函数接受float)...但我确实认为使用friend更好。

答案 2 :(得分:0)

当你定义这样的东西时,

inline const Fixed operator - (float f) const;

你说我希望这个操作符(你在类中)对特定类型进行操作,例如在这里浮动。

而友元二元运算符意味着两种类型之间的操作。

class Fixed
{
    inline friend const Fixed operator-(const Fixed& first, const float& second);
};

inline const Fixed operator-(const Fixed& first, const float& second)
{
    // Your definition here.
}

与朋友操作员一起,您可以将自己的班级放在操作员的任何一侧。

答案 3 :(得分:0)

通常,算术运算的自由函数运算符优于实现成员函数。主要原因是你现在面临的问题。编译器将以不同方式处理左侧和右侧。请注意,虽然严格的OO关注者将只考虑其类接口的类花括号内部的那些方法,但专家已经argued而不是C ++中的情况。

如果自由功能操作员需要访问私人会员,请成为操作员朋友。毕竟如果它是在同一个头文件中提供的(遵循上面的Sutter的基本原理)那么它就是该类的一部分。

如果你真的想避免使用它并且不介意使你的代码不那么惯用(因此维护性较差),你可以提供一个公共方法来完成实际工作并从操作员发送到该方法。

class Fixed {
private:
   Fixed();
   Fixed( double d ); // implicit conversion to Fixed from double

   Fixed substract( Fixed const & rhs ) const;
// ...
};

Fixed operator-( Fixed const & lhs, Fixed const & rhs )
{
   return lhs.substract( rhs );
}

在上面的代码中,您可以减去Fixed - FixedFixed - doubledouble - Fixed。编译器将找到自由函数并隐式转换(在示例中通过double构造函数)双精度到Fixed个对象。

虽然这对于算术运算符来说是单一的,但它接近于证明多态转储运算符的惯用方法。因此,虽然不是最自然的解决方案,但它不会是最令人惊讶的代码

// idiomatic polymorphic dump operator
class Base {
public:
   virtual std::ostream& dump( std::ostream & ) const;
};
std::ostream& operator<<( std::ostream& o, Base const & d )
{
   return d.dump( o );
}