I have some basic questions on operator overloading in C++.
I am reading "Thinking in C++, volume 1" and it says that operators that can't be overloaded are member selection operator . , member dereference operator * . So it looks like other operators can be overloaded
Can I overload & operator? But won't this confuse compiler, if I implement it as prefix operator or does it then hide its default meaning?
In code to reference public members, I do sth like ptr -> data_member
I understand that compiler takes care of it and binds this reference with the data member, so this is not seen at all at runtime and -> does not come across as operator to run-time. Is this understanding correct?
As an aside, virtual concept exists only for member functions and not for data members, right? So at runtime if a Base pointer points to Derived class, I can not access members of Derived class from this pointer?
can I overload -> operator? But won't this confuse compiler, if I overload it as prefix operator
If question is not articulated properly, let me know.
are there some in-built operator overloading for each class by compiler etc
overloading operator -> is very useful, especially when creating a smart pointer class.
overloading an operator is the same as overloading any other function. The compiler knows what to use based on the parameters you specified. So, no, the compiler won't get confused. You on the other hand, may get confused. This is one of the reasons why a lot of programmers say to avoid overloading operators (I disagree with this, depending on the case).
Recall that operator& has two meanings: bit-wise and, and address of.
Likewise, you can overload operator*, as multiplication, but not for pointers.
As for confusing the compiler, all modern C++ compilers will throw a compiler error when you do something that is ambiguous.
And just because you can overload an operator, doesn't mean you should; it's really easy to screw up some operators, and cause all kinds of really hard to find bugs.
Related
Why we cannot overload subscript operator using friend function
Not all operators can be overloaded. For those that can be overloaded there are rules whether they can be non-members or not. For details I refer you to https://en.cppreference.com/w/cpp/language/operators.
The operators that can be overloaded only as members are =,(),[] and ->.
Let's add overloading to a language that is derived from C. Did you know that the built-in operator is symmetric? For raw arrays it's always true that a[i] == i[a]. It's an emergent property that may be cute, but hardly conducive for great code.
So, wouldn't it be a good idea to limit our overloading to only have our "container object" on the left? Many will say yes. And it is also true that the left-hand side of an expression is always taken as this for member lookup.
Now if we only want to allow the object on the lhs, the choice is easy: only allow defining the operator as a member. For a similar reason operator= is a member-only operator too, because the object being assigned to only makes sense (to the language designer) on the left.
Can we use operator overloading on non-objects? e.g adding two vector<int>. Or do I need to make a class having vector<int> and then overload the + operator.
Also, I want to know if it is possible to have 2 different types of operands in a binary operator. If yes, how?
Can we use operator overloading on non-objects?
Operators are essentially implemented as functions and can accept a similar range of types as functions do.
There are a few restrictions, in particular, it is limited by the fact that at least one argument must be a class or enum (user or library type).
In addition, there are some operators that have to be implemented as member functions, so they will be associated with a class, but many can be declared and defined outside of a class.
The exact number of arguments is determined by the operator being implemented. Unary operators accept 1 argument, binary operators accept 2 arguments.
Generally, the advice is to offer behaviour similar to the canonical forms, but the language itself does not limit your implementation to just that behaviour.
Also, I want to know if it is possible to have 2 different types of operands in a binary operator. If yes, how?
Yes. Again, just as functions do.
Can we use operator overloading on non-objects? e.g adding two vector.
I think you meant to ask whether it's possible to use operator overloading outside of a class definition. And yes, it definitely is possible as already stated by other people here. Here's a simple example of overloading the '+' operator for a custom 'MyClass' type:
MyClass operator+(const MyClass& a, const MyClass& b)
{
return MyClass(a.value + b.value);
}
However, there is a reason why std::vector doesn't have overloads for arithmetic operators as the result is ambiguous. Do you want '+' to link two vectors together or should the components be added? How do you handle vectors which differ in size? Often, containers of the STL provide special methods for these purposes and don't rely on operators when it's not clear so make sure you've checked the specification of std::vectors
You're free to implement such operators as you see fit, especially with your own types but you risk creating unintuitive behaviour and ultimately, bad code. In the case of std::vector, it might be better to write a function with a descriptive name which operates on std::vector in the way you desire:
std::vector<int> addComponents(const std::vector<int>& a, const std::vector<int>& b);
Can we use operator overloading on non-objects? e.g adding two vector[?]
The premise of your question is broken, because a vector<int> is an object.
Overload all you like.
Also, i want to know if it is possible to have 2 different types of operands in a binary operator.
Try it.
If yes, how?
By typing it.
It is reasonable that sizeof and typeid cannot be overloaded, but I can't see the harm in overloading ?:, .* and .. Are there technical reasons for this?
To quote Bjarne Stroustrup:
There is no fundamental reason to
disallow overloading of ?:. I just
didn't see the need to introduce the
special case of overloading a ternary
operator. Note that a function
overloading expr1?expr2:expr3 would
not be able to guarantee that only one
of expr2 and expr3 was executed.
...
Operator . (dot) could in principle be
overloaded using the same technique as
used for ->. However, doing so can
lead to questions about whether an
operation is meant for the object
overloading . or an object referred to
by . ... This problem can be solved in
several ways. At the time of
standardization, it was not obvious
which way would be best.
Source
If you overload ., how would you access class members? What would be the meaning of obj.data?
What would the syntax be?
In fact, there are good reasons for not overloading any operator
which doesn't evaluate all of its operands: you shouldn't
overload && or || either (except in special cases). You can't
simulate this with an overloaded operator. Consider something
like:
p != NULL ? defaultValue : p->getValue()
where the type of defaultValue or p->getValue() causes overload
resolution to pick up your overload. It's a common idiom, but
it can't be made to work if you overloaded ?:.
Here's some reading material C++ FAQ Lite :)
In general there would be no benefit to overloading the operators above. What additional semantics would you be trying to implement?
The reason for overloading operators is to provide intuitive syntax to the user of your class. For example, it makes sense to overload + and += for strings. It's obvious to another developer what that means.
It's really not obvious what you would overload ?: for ... That said there are no technical reasons I am aware of that prevented these operators from being overloaded.
Overloading the -> operator allows for reference counted pointers to be created such as boost::shared_ptr. The concept of 'negating' an object might have different meanings in different contexts, so it's reasonable to occasionally overload this operator.
Defining "operator bool" is enough for ?: to work.
For operator . think of this: SomeClass."SomeString!!"
These overloadings prohibit compiler's lexer from parsing the file correctly.
The reason you can overload most operators is to be able to simulate built in types. Since none of the built in types can use the . operator, it wouldn't serve any purpose. operator* and operator-> are there so you can make your own pointer classes. All the math and boolean operators are there to be able to make your own numeric classes.
The C++ standard defines the expression using subscripts as a postfix expression. AFAIK, this operator always takes two arguments (the first is the pointer to T and the other is the enum or integral type). Hence it should qualify as a binary operator.
However MSDN and IBM does not list it as a binary operator.
So the question is, what is subscript operator? Is it unary or binary? For sure, it is not unary as it is not mentioned in $5.3 (at least straigt away).
What does it mean when the Standard mentions it's usage in the context of postfix expression?
I'd tend to agree with you in that operator[] is a binary operator in the strictest sense, since it does take two arguments: a (possibly implicit) reference to an object, and a value of some other type (not necessarily enumerated or integral). However, since it is a bracketing operator, you might say that the sequence of tokens [x], where x might be any valid subscript-expression, qualifies as a postfix unary operator in an abstract sense; think currying.
Also, you cannot overload a global operator[](const C&, size_t), for example. The compiler complains that operator[] must be a nonstatic member function.
You are correct that operator[] is a binary operator but it is special in that it must also be a member function.
Similar to operator()
You can read up on postfix expressions here
I just found an interesting article about operator[] and postfix expression, here
I think it's the context that [] is used in that counts. Section 5.2.1 the symbol [] is used in the context of a postfix expression that is 'is identical (by definition) to *((E1)+(E2))'. In this context, [] isn't an operator. In section 13.5.5 its used to mean the subscripting operator. In this case it's an operator that takes one argument. For example, if I wrote:
x = a[2];
It's not necessarily the case that the above statement evaluates to:
x = *(a + 2);
because 'a' might be an object. If a is an object type then in this context, [] is used as an subscript operator.
Anyway that's the best explanation I can derive from the standard that resolves apparent contradictions.
If you take a close look to http://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B it will explain you that standard C++ recognize operator[] to be a binary operator, as you said.
Operator[] is, generally speaking, binary, and, despite there is the possibility to make it unary, it should always be used as binary inside a class, even because it has no sense outside a class.
It is well explained in the link I provided you...
Notice that sometimes many programmers overload operators without think too much about what they are doing, sometimes overloading them in an incorrect manner; the compiler is ease is this and accept it, but, probably, it was not the correct way to overload that operator.
Following guides like the one I provided you, is a good way to do things in the correct manner.
So, always beware examples where operators are overloaded without a good practice (out of standard), refer, first to the standard methods, and use those examples that are compliant to them.
Why can some operators only be overloaded as member functions, other as non-member "free" functions and the rest of them as both?
What is the rationale behind those?
How to remember which operators can be overloaded as what (member, free, or both)?
The question lists three classes of operators. Putting them together on a list helps, I think, with understanding why a few operators are restricted in where they can be overloaded:
Operators which have to be overloaded as members. These are fairly few:
The assignment operator=(). Allowing non-member assignments seems to open the door for operators hijacking assignments, e.g., by overloading for different versions of const qualifications. Given that assignment operators are rather fundamental that seems to be undesirable.
The function call operator()(). The function call and overloading rules are sufficiently complicated as is. It seems ill-advised to complicate the rules further by allowing non-member function call operators.
The subscript operator[](). Using interesting index types it seems that could interfere with accesses to operators. Although there is little danger of hijacking overloads, there doesn't seem to be much gain but interesting potential to write highly non-obvious code.
The class member access operator->(). Off-hand I can't see any bad abuse of overloading this operator a non-member. On the other hand, I also can't see any. Also, the class member access operator has rather special rules and playing with potential overloads interfering with these seems an unnecessary complication.
Although it is conceivable to overload each of these members are a non-member (especially the subscript operator which is works on arrays/pointers and these can be on either side of the call) it seems surprising if, e.g., an assignment could be hijacked by a non-member overload which which is a better match than one of the member assignments. These operators are also rather asymmetric: you generally wouldn't want to support conversion on both sides of an expression involving these operators.
That said, e.g., for a lambda expression library it would be nice if it were possible to overload all of these operators and I don't think there is an inherent technical reason to preventing these operators from being overloadable.
Operators which have to be overloaded as non-member functions.
The user-defined literal operator"" name()
This operator is somewhat of an odd-ball and, arguably not really really an operator. In any case, there is no object to call this member on for which members could be defined: the left argument of user-defined literals are always built-in types.
Not mentioned in the question but there are also operator which can't be overloaded at all:
The member selector .
The pointer-to-member object access operator .*
The scope operator ::
The ternary operator ?:
These four operators were considered to be too fundamental to be meddled with at all. Although there was a proposal to allow overloading operator.() at some point there isn't strong support doing so (the main use case would be smart references). Although there are certainly some contexts imaginable where it would be nice to overload these operators, too.
Operators which can be overloaded either as members or as non-members. This is the bulk of the operators:
The pre- and post-increment/-decrement operator++(), operator--(), operator++(int), operator--(int)
The [unary] dereference operator*()
The [unary] address-of operator&()
The [unary] signs operator+(), operator-()
The logical negation operator!() (or operator not())
The bitwise inversion operator~() (or operator compl())
The comparisons operator==(), operator!=(), operator<(), operator>(), operator<=(), and operator>()
The [binary] arithmetic operator+(), operator-(), operator*(), operator/(), operator%()
The [binary] bitwise operator&() (or operator bitand()), operator|() (or operator bit_or()), operator^() (or operator xor())
The bitwise shift operator<<() and operator>>()
The logic operator||() (or operator or()) and operator&&() (or operator and())
The operation/assignment operator#=() (for # being a suitable operator symbol()
The sequence operator,() (for which overloading actually kills the sequence property!)
The pointer pointer-to-member access operator->*()
The memory management operator new(), operator new[](), operator new[](), and operator delete[]()
The operators which can be overloaded either as members or as non-members are not as necessary for fundamental object maintenance as the other operators. That is not to say that they are not important. In fact, this list contains a few operators where it is rather questionable whether they should be overloadable (e. g., the address-of operator&() or the operators which normally cause sequencing, i. e., operator,(), operator||(), and operator&&().
Of course, the C++ standard doesn't give a rationale on why things are done the way they are done (and there are also no records of the early days when these decisions where made). The best rationale can probably be found in "Design and Evolution of C++" by Bjarne Stroustrup. I recall that the operators were discussed there but there doesn't seem to be an electronic version available.
Overall, I don't think there are really strong reasons for the restrictions other than potential complication which was mostly not considered worth the effort. I would, however, doubt that the restrictions are likely to be lifted as the interactions with existing software are bound to change the meaning of some program in unpredictable ways.
The rationale is that it would not make sense for them to be non-members, as the thing on the left-hand side of the operator must be a class instance.
For example, assuming a class A
A a1;
..
a1 = 42;
The last statement is really a call like this:
a1.operator=(42);
It would not make sense for the thing on the LHS of the . not to be an instance of A, and so the function must be a member.
Because you can't modify the semantics of primitive types. It wouldn't make sense to define how operator= works on an int, how to deference a pointer, or how an array access works.
Here is one example:
When you are overloading the << operator for a class T the signature will be:
std::ostream operator<<(std::ostream& os, T& objT )
where the implementation needs to be
{
//write objT to the os
return os;
}
For the << operator the first argument needs to be the ostream object and the second argument your class T object.
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 binary operator member functions can only take one argument and the invoking object is implicitly passed in as the first argument using this.
If you use the std::ostream operator<<(std::ostream& os) signature as a member function you will actually end up with a member function std::ostream operator<<(this, std::ostream& os) which will not do what you want.
Therefore you need a operator that is not a member function and can access member data (if your class T has private data you want to stream, operator<< needs to be a friend of class T).