Why can some operators be overloaded only as member functions, others as friends functions, and others like both?

Why can some operators be overloaded only as member functions, others as β€œfree” functions that are not members, and others how?

What is the reason for this?

How to remember which operators can be overloaded like that (member, free or both)?

+42
c ++ class operator-overloading member-functions
Jul 15 '09 at 16:55
source share
4 answers

The question lists three classes of operators. I think combining them into a list helps to understand why several operators are limited to where they can be overloaded:

  • Operators that must be overloaded as members. There are quite a few of them:

    • Purpose operator=() . Allowing assignments other than members seems to open the door for operators to capture operators, for example, by overloading different versions of const qualifications. Given that assignment operators are quite fundamental, which seems undesirable.
    • Calling the operator()() function. The rules for calling a function and overloading are quite complicated as they are. It seems that it is not recommended to extend the rules further, allowing non-member operator operators.
    • The subscript operator[]() . Using interesting types of indexes seems to hinder access to operators. Although there is a small risk of overload capture, there seems to be no big gain, but there is an interesting potential for writing very unobvious code.
    • Access to an element of class operator->() . Out of my hands, I do not see a bad abuse of the overload of this non-member operator. On the other hand, I don’t see either. In addition, the operator for accessing a member of a class has rather special rules, and playing with potential overloads that prevent them seems an unnecessary complication.

    Although overloading is possible, each of these members is non-member (especially the subscript operator that works with arrays / pointers, and this can be on either side of the call) it seems surprising if, for example, the destination can be captured by a non-member overload, which better matches one of the member appointments. These operators are also quite asymmetric: as a rule, you would not want to support the conversion on both sides of an expression that includes these operators.

    However, for example, for a lambda expression library, it would be nice if all of these operators could be overloaded, and I don't think there is some technical reason to prevent these operators from being overloaded.

  • Operators that must be overloaded as non-member functions.

    • User literal operator"" name()

    This operator is somewhat odd and perhaps not really an operator. In any case, there is no object to call this member for which members can be defined: the left argument of user-defined literals is always inline.

  • Not mentioned in the question, but there is an operator that cannot be overloaded at all:

    • Element selector .
    • Object access operator with a pointer to an element .*
    • Area Operator ::
    • Ternary operator ?:

    These four operators were considered too fundamental to even intervene. Although there is a suggestion to allow operator.() Overloading, at some point there is no strong support that does this (the main use case will be an intelligent link). Although, of course, there are some contexts that you could imagine where it would be nice to overload these operators.

  • Operators that can be overloaded either as members or as non-members. This is the main part of the operators:

    • Pre-and post-increment / -restriction operator++() , operator--() , operator++(int) , operator--(int)
    • [unary] dereference operator*()
    • [unary] address operator&()
    • [unary] signs operator+() , operator-()
    • Logical negation of operator!() (Or operator not() )
    • Bitwise inversion of operator~() (or operator compl() )
    • Comparison of operator==() , operator!=() , operator<() , operator>() , operator<=() and operator>()
    • [binary] arithmetic operator+() , operator-() , operator*() , operator/() , operator%()
    • [binary] bitwise operator&() (or operator bitand() ), operator|() (or operator bit_or() ), operator^() (or operator xor() )
    • Bitwise shift operator<<() and operator>>()
    • Logic operator||() (or operator or() ) and operator&&() (or operator and() )
    • Operation / assignment operator@=() (for @ is a suitable operator () character
    • Sequence operator,() (for which overloading actually kills the sequence property!)
    • Pointer to an operator->*() element operator->*()
    • Memory management operator new() , operator new[]() , operator new[]() and operator delete[]()

    Operators that can be overloaded either as members or as non-members are not necessary for servicing core objects, like other operators. This does not mean that they are not important. In fact, this list contains several operators, where it is rather doubtful whether they should be overloaded (for example, the address operator&() or operators that usually call a sequence, i.e. operator,() , operator||() and operator&&() .

Of course, the C ++ standard gives no reason to explain why everything is done the way it is done (and also there are no records of the early days when these decisions are made). The best justification can probably be found in Bjarne Straustrup's Design and Evolution of C ++. I remember that the operators were discussed there, but there seems to be no electronic version available.

In general, I do not think that there are serious reasons for limitations other than potential complications that are generally not considered worthy of attention. However, I would doubt that the restrictions are likely to be lifted, since interaction with existing software will inevitably change the meaning of any program in unpredictable ways.

+28
Dec 16 '13 at 22:04
source share

The rationale is that it would not make sense for them to be non-member, since the item on the left side of the operator should be an instance of the class.

For example, assuming class A

 A a1; .. a1 = 42; 

The last statement is really a call like this:

 a1.operator=(42); 

It would not make sense if the subject was LHS . was not an instance of A, so the function must be a member.

+7
Jul 15 '09 at 16:56
source share

Because you cannot change the semantics of primitive types. It makes no sense to determine how operator= works on int , how to relate to a pointer, or how array access works.

+5
Jul 15 '09 at 16:58
source share

Here is one example: When you overload the << operator for class T , the signature will be:

 std::ostream operator<<(std::ostream& os, T& objT ) 

where the implementation should be

 { //write objT to the os return os; } 

For the << operator, the first argument must be an ostream object, and the second should be an object of class T.

If you try to define operator<< as a member function, you will not be allowed to define it as std::ostream operator<<(std::ostream& os, T& objT) . This is because member functions of a binary operator can take only one argument, and the caller is implicitly passed as the first argument using this .

If you use the signature std::ostream operator<<(std::ostream& os) as a member function, you will actually get the member function std::ostream operator<<(this, std::ostream& os) , which will not do what you want. Therefore, you need an operator that is not a member function and can access member data (if your class T has the personal data you want to pass, operator<< must be a friend of class T).

0
Dec 15 '13 at 12:37
source share



All Articles