Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

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?

share|improve this question

3 Answers 3

up vote 13 down vote accepted

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;
}
share|improve this answer
    
What does your example illustrate? Are you returning a smart pointer to a string and somehow outputting the size? I'm confused. –  Trevor Hickey Sep 21 '12 at 19:48
1  
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. –  Lars Viklund Sep 22 '12 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 Apr 14 '13 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. –  Lars Viklund Mar 20 at 15:05

"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.

share|improve this answer

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.

share|improve this answer
    
I didn't realize standard library containers implemented ->, or that it was overloadable. –  Jakob Weisblat Jul 19 '12 at 15:24
2  
C++03 24.1/1 requires that any iterator where (*i).m is valid must support i->m with the same semantics. –  Lars Viklund Jul 19 '12 at 15:25

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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