简体   繁体   English

定义适当的减法运算符

[英]Defining a proper subtraction operator

I wrote an abstraction class for a math object, and defined all of the operators. 我为数学对象编写了一个抽象类,并定义了所有运算符。 While using it, I came across: 使用时,我遇到了:

Fixed f1 = 5.0f - f3;

I have only two subtraction operators defined: 我只定义了两个减法运算符:

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

I get what is wrong here - addition is swappable (1 + 2 == 2 + 1) while subtraction is not (same goes for multiplication and division). 我在这里弄错了-加法是可交换的(1 + 2 == 2 + 1),而减法则不是(乘法和除法也是如此)。 I immediately wrote a function outside my class like this: 我立即编写了一个函数,如下所示:

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

But then I realized this cannot be done, because to do that I would have to touch the class's privates, which results to using the keyword friend which I loath, as well as polluting the namespace with a 'static' unnecessary function. 但是后来我意识到这是无法完成的,因为要做到这一点,我将不得不接触类的私有对象,这导致使用了我讨厌的关键字friend ,以及使用“静态”不必要的函数污染了名称空间。

Moving the function inside the class definition yields this error in gcc-4.3: 在类定义内移动函数会在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

Doing as GCC suggested, and making it a non-static function results the following error: 按照GCC的建议进行操作,并将其设置为非静态函数会导致以下错误:

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

Why can't I define the same operator inside the class definition? 为什么我不能在类定义中定义相同的运算符? if there's no way to do it, is there anyway else not using the friend keyword? 如果没有办法,是否还有其他不使用friend关键字的东西?

Same question goes for division, as it suffers from the same problem. 同样的问题也适用于除法,因为它遭受了同样的问题。

If you need reassuring that friend functions can be OK: 如果您需要确保朋友功能可以正常运行:

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

Which operations need access to internal data we would otherwise have to grant via friendship? 哪些操作需要访问我们否则必须通过友谊授予的内部数据? These should normally be members. 这些通常应该是成员。 (There are some rare exceptions such as operations needing conversions on their left-hand arguments and some like operator<<() whose signatures don't allow the *this reference to be their first parameters; even these can normally be nonfriends implemented in terms of (possibly virtual) members, but sometimes doing that is merely an exercise in contortionism and they're best and naturally expressed as friends.) (有一些罕见的例外,例如需要在其左手参数上进行转换的操作,以及一些类似operator <<()的操作,其签名不允许* this引用为它们的第一个参数;即使这些通常可以用术语实现,也可以是非友善的(可能是虚拟的)成员,但有时这样做只是扭曲主义的一种练习,他们最好且自然地表现为朋友。)

You are in the "operations needing conversions on the left-hand arguments" camp. 您正处于“需要在左侧参数上进行转换的操作”阵营中。 If you don't want a friend, and assuming you have a non-explicit float constructor for Fixed , you can implement it as: 如果您不想要朋友,并且假设您为Fixed拥有一个非显式的float构造函数,则可以将其实现为:

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

then implement minus as a public member function, that most users won't bother with because they prefer the operator. 然后将minus实现为公共成员函数,大多数用户不会因为他们喜欢运算符而烦恼。

I assume if you have operator-(float) then you have operator+(float) , so if you don't have the conversion operator, you could go with: 我假设如果您有operator-(float)那么您有operator+(float) ,因此,如果您没有转换运算符,则可以使用:

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

Or just Fixed(lhs) - rhs if you have an explicit float constructor. 或者只是Fixed(lhs) - rhs如果您有显式的float构造函数)。 Those may or may not be as efficient as your friend implementation. 那些效率可能不如您的朋友实现有效。

Unfortunately the language is not going to bend over backwards to accommodate those who happen to loathe one of its keywords, so operators can't be static member functions and get the effects of friendship that way ;-p 不幸的是,该语言不会向后弯曲以适应那些偶然讨厌其关键字之一的人,因此运算符不能是静态成员函数,而不能以这种方式获得友谊的影响;-p

  1. "That's what friends are for..." “这就是朋友的作用...”
  2. You could add an implicit conversion between float and your type (eg with a constructor accepting float )... but I do think using a friend is better. 您可以在float和您的类型之间添加隐式转换(例如,使用接受float的构造函数)...但是我确实认为使用friend会更好。

When you define something like this, 当您定义这样的内容时,

inline const Fixed operator - (float f) const;

you are saying that I want this operator(you are inside the class) to operate on a specific type, float here for example. 您是说我希望此运算符(您在类中)对特定类型进行操作,例如,在此处浮动。

Whereas a friend binary operator, means an operation between two types. 友善的二元运算符是指两种类型之间的运算。

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.
}

with friend operators you can have your class on either side of the operator it self. 与朋友接线员一起,您可以将自己的班级放在接线员的任一侧。

In general, free function operators for arithmetic operations are better than implementing member functions. 通常,用于算术运算的自由函数运算符要好于实现成员函数。 The main reason is the problem you are facing now. 主要原因是您现在面临的问题。 The compiler will treat the left and right sides differently. 编译器将左右区别对待。 Note that while strict OO followers will consider only those methods inside the class curly braces part of its interface, but it has been argued by experts that not to be the case in C++. 请注意,虽然严格的OO追随者只考虑它的接口的类的花括号里面的一部分的方法,但它已被认为专家是不是在C ++而言。

If the free function operator requires access to private members, make the operator friend. 如果自由功能操作员需要访问私有成员,请将该操作员交朋友。 After all if it is provided in the same header file (following Sutter's rationale above) then it is part of the class. 毕竟,如果它在同一头文件中提供(遵循上面的Sutter原理),则它是该类的一部分。

If you really want to avoid it and don't mind making your code less idiomatic (and thus less maintainable) you can provide a public method that does the real work and dispatch to that method from the operator. 如果您真的想避免这种情况,并且不介意使您的代码减少惯用性(从而减少了可维​​护性),则可以提供一个执行实际工作并从操作员分派给该方法的公共方法。

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 );
}

In the code above, you can substract Fixed - Fixed , Fixed - double , double - Fixed . 在上面的代码中,您可以减去Fixed - FixedFixed - doubledouble - Fixed The compiler will find the free function and implicitly convert (in the example through the double constructor) the doubles into Fixed objects. 编译器将找到free函数,并将double隐式转换(在示例中通过double构造函数),将double转换为Fixed对象。

While this is unidiomatic for arithmetic operators, it is close to the idiomatic way of proving a polymorphic dump operator. 虽然对于算术运算符来说这是单语的,但它接近证明多态转储运算符的惯用方式。 So while not being the most natural solution it won't be the most surprising code around either 因此,虽然不是最自然的解决方案,但也不会是最令人惊讶的代码

// 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 );
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM