difference between global operator and member operator

前端 未结 5 700
一整个雨季
一整个雨季 2020-12-02 12:43

Is there a difference between defining a global operator that takes two references for a class and defining a member operator that takes only the right operand?

Glob

相关标签:
5条回答
  • 2020-12-02 12:46

    Your smartest option is to make it a friend function.

    As JaredPar mentions, the global implementation cannot access protected and private class members, but there's a problem with the member function too.

    C++ will allow implicit conversions of function parameters, but not an implicit conversion of this.

    If types exist that can be converted to your X class:

    class Y
    {
    public:
        operator X();  // Y objects may be converted to X
    };
    
    
    X x1, x2;
    Y y1, y2;
    

    Only some of the following expressions will compile with a member function.

    x1 == x2;   // Compiles with both implementations
    x1 == y1;   // Compiles with both implementations
    y1 == x1;   // ERROR!  Member function can't convert this to type X
    y1 == y2;   // ERROR!  Member function can't convert this to type X
    

    The solution, to get the best of both worlds, is to implement this as a friend:

    class X
    {
        int value;
    
    public:
    
        friend bool operator==( X& left, X& right ) 
        {
            return left.value == right.value;
        };
    };
    
    0 讨论(0)
  • 2020-12-02 12:46

    Here's a real example where the difference isn't obvious:

    class Base
    {
    public:
        bool operator==( const Base& other ) const
        {
            return true;
        }
    };
    
    class Derived : public Base
    {
    public:
        bool operator==( const Derived& other ) const
        {
            return true;
        }
    };
    
    Base() == Derived(); // works
    Derived() == Base(); // error
    

    This is because the first form uses equality operator from base class, which can convert its right hand side to Base. But the derived class equality operator can't do the opposite, hence the error.

    If the operator for the base class was declared as a global function instead, both examples would work (not having an equality operator in derived class would also fix the issue, but sometimes it is needed).

    0 讨论(0)
  • 2020-12-02 12:53

    One reason to use non-member operators (typically declared as friends) is because the left-hand side is the one that does the operation. Obj::operator+ is fine for:

    obj + 2
    

    but for:

    2 + obj
    

    it won't work. For this, you need something like:

    class Obj
    {
        friend Obj operator+(const Obj& lhs, int i);
        friend Obj operator+(int i, const Obj& rhs);
    };
    
    Obj operator+(const Obj& lhs, int i) { ... }
    Obj operator+(int i, const Obj& rhs) { ... }
    
    0 讨论(0)
  • 2020-12-02 12:53

    To sum up to the answer by Codebender:

    Member operators are not symmetric. The compiler cannot perform the same number of operations with the left and right hand side operators.

    struct Example
    {
       Example( int value = 0 ) : value( value ) {}
       int value;
    
       Example operator+( Example const & rhs ); // option 1
    };
    Example operator+( Example const & lhs, Example const & rhs ); // option 2
    int main()
    {
       Example a( 10 );
       Example b = 10 + a;
    }
    

    In the code above will fail to compile if the operator is a member function while it will work as expected if the operator is a free function.

    In general a common pattern is implementing the operators that must be member functions as members and the rest as free functions that delegate on the member operators:

    class X
    {
    public:
       X& operator+=( X const & rhs );
    };
    X operator+( X lhs, X const & rhs )
    {
       lhs += rhs; // lhs was passed by value so it is a copy
       return lhs;
    }
    
    0 讨论(0)
  • 2020-12-02 13:00

    There is at least one difference. A member operator is subject to access modifiers and can be public, protected or private. A global member variable is not subject to access modifier restrictions.

    This is particularly helpful when you want to disable certain operators like assignment

    class Foo { 
      ...
    private:
      Foo& operator=(const Foo&); 
    };
    

    You could achieve the same effect by having a declared only global operator. But it would result in a link error vs. a compile error (nipick: yes it would result in a link error within Foo)

    0 讨论(0)
提交回复
热议问题