In c++, the * operator can be overloaded, such as with an iterator, but the arrow (->) (.*) operator does not work with classes that overload the * operator. I imagine that the preprocessor could easily replace all instances of -> with (*left).right, and that would make iterators nicer to implement. is there a practical reason for -> to be different, or is that just a peculiarity of the language/designers?
0
The rule that foo->bar
equals (*foo).bar
only holds for the builtin operators.
Unary operator *
doesn’t always have the pointer dereference semantics. I could make a library in which it means matrix transposition, zero-or-more parser matches, or pretty much anything.
It would make the language more bothersome if anything that overloads unary operator *
would suddenly gain an operator ->
you did not ask for, with semantics that might not make sense.
operator ->
is separately overloadable, so if you want one, you can overload one with minimal effort.
Also note that such an overload would have some rather interesting properties, like automatically chaining operator ->
calls until one in the chain returns a raw pointer. This is quite useful for smart pointers and other proxy types.
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <string>
#include <iostream>
#include <ostream>
struct Foo
{
boost::shared_ptr<std::string> operator -> () const
{
return boost::make_shared<std::string>("trololo");
}
};
int main()
{
Foo foo;
std::cerr << foo->size() << std::endl;
}
4
“The C++ Programming Language” describes the fact that these operators are different so that they can be, but also says:
If you provide more than one of these operators, it might be wise to provide the equivalence, just as it is wise to ensure that
++x
andx+=1
have the same effect asx=x+1
for a simple variablex
of some class if ++,+=,=, and + are provided.
So it seems that the language designers provided separate overload points because you might want to overload them differently, rather than assuming that you always want them to be the same.
As a general rule, C++ is designed to favor flexibility, so overloads of *
and ->
are separate. Though it’s fairly unusual to do so, if you want to badly enough you can write those overloads to do entirely different things (e.g., might make sense for a domain specific language implemented inside of C++).
That said, iterators do support either usage. On ancient implementations, you might find a library that requires (*iter).whatever
instead of iter->whatever
, but if so, that’s a bug in the implementation, not a characteristic of the language. Given the amount of work involved in implementing all the standard containers/algorithms/iterators, it’s not surprising that some early releases were somewhat incomplete, but they were never really intended to be that way.
2