19

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

3 Answers 3

17

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
  • What does your example illustrate? Are you returning a smart pointer to a string and somehow outputting the size? I'm confused. Commented Sep 21, 2012 at 19:48
  • 2
    It illustrates the last paragraph of my answer, how using the -> operator chains until it gets a raw pointer to something, dereferencing and accessing a member of it. If operator -> didn't chain, the example would be ill-formed as a shared_ptr is not a raw pointer. Commented Sep 22, 2012 at 18:55
  • @LarsViklund: your answer has a problem: you said "operator-> ... automatically chains operator-> calls until one in the chain returns a raw pointer". This is not correct - using A->B syntax chains at most 1 additional call. What the C++ -> binary syntax actually does is not call the object's opeartor-> directly - instead it looks at the type of A and check if its a raw pointer. If it is then -> derefs it and execute B on that, otherwise it calls the object's operator->, derefs the result (either using native raw pointer or another operator-> and then executes B on the result
    – Guss
    Commented Apr 14, 2013 at 11:03
  • @Guss: I cannot find any chapter and verse for your claim, nor reproduce it in a compiler. C++11 13.5.6/1 indicates that if a suitable overload exists, x->m shall be interpreted as (x.operator->())->m. If the LHS is something that has a suitable overload of operator-> again, this process recurs until there's just the usual (*x).m effect of 5.2.5/2. Commented Mar 20, 2014 at 15:05
9

"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 and x+=1 have the same effect as x=x+1 for a simple variable x 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.

8

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
  • I didn't realize standard library containers implemented ->, or that it was overloadable.
    – aaazalea
    Commented Jul 19, 2012 at 15:24
  • 4
    C++03 24.1/1 requires that any iterator where (*i).m is valid must support i->m with the same semantics. Commented Jul 19, 2012 at 15:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.