views:

191

answers:

5

Wouldn't it make sense if p->m was just syntactic sugar for (*p).m? Essentially, every operator-> that I have ever written could have been implemented as follows:

Foo::Foo* operator->()
{
    return &**this;
}

Is there any case where I would want p->m to mean something else than (*p).m?

A: 

You might want to do some other operations, like incrementing a variable (access counting) or even some safety checks etc. On the other hand, i had never need to overload the operator-> ...

PeterK
A: 

I think it's used for shared_ptr<> in boost (for example). That makes them "look" like normal pointers, although they're special pointers (reference counting afaik).

Christopher Bertels
That doesn't tell me anything interesting I'm afraid. Why does `shared_ptr` need to overload `operator->()`, rather than simply overload `operator*()` and let the compiler rewrite `p->m` to `(*p).m`? This has the obvious advantage of consistency -- you could use either syntax and get the same results, which is surely what everyone expects. So why isn't it done this way?
j_random_hacker
@j_random_hacker +1 That's exactly my point!
FredOverflow
+3  A: 

One use case might be when you're creating a DSL inside C++, along the lines of Boost Karma (although it does not seem to overload -> in their library), where you completely change the meaning of the traditional C++ operators. Whether that's a good idea is certainly up to debate.

Mark Rushakoff
+1 for the reason itself, but that feels like a terrible idea to me... About as bad as writing a copy ctor that changes the state of the world (since sometimes the compiler can choose to call it or not).
j_random_hacker
+10  A: 

operator->() has the bizarre distinction of implicitly being invoked repeatedly while the return type allows it. The clearest way to show this is with code:

struct X {
    int foo;
};

struct Y {
    X x;
    X* operator->() { return &x; }
};

struct Z {
    Y y;
    Y& operator->() { return y; }
};

Z z;
z->foo = 42;          // Works!  Calls both!

I recall an occasion when this behaviour was necessary to enable an object to behave as a proxy for another object in a smart-pointer-like context, though I can't remember the details. What I do remember is that I could only get the behaviour to work as I intended using the a->b syntax, by using this strange special case; I could not find a way to get (*a).b to work similarly.

Not sure that this answers your question; really I'm saying, "Good question, but it's even weirder than that!"

j_random_hacker
+1 But the language could still enforce this weird rule even if `operator->` was hardwired into the language, right? Technically it would be slightly more than syntactic sugar -- *syntactic syrup* maybe?
FredOverflow
Do you mean if the language spec asked the compiler to rewrite `a->b` to `(*a).b`, would it still make sense to recursively invoke `operator*()`? No, since it's sometimes necessary to stop before you "get to the end" (i.e. return a pointer/pointer-like object, e.g. so you can modify it, instead of returning the final pointed-to object). The `operator->()` recursion weirdness is possible (necessary?) because it's not really a "proper" 2-arg operator -- the 2nd "argument" must be a `struct` field name, which is something that can't be expressed in the C++ type system.
j_random_hacker
P.S: I would call it *syntactic molasses* -- slowing my understanding to a crawl... :P
j_random_hacker
+1  A: 

Regular pointers offer p->m and (*p).m, with p->m being by far the more common. If you only allowed overloading one of them, then it would be inconsistent with the default type. As for why not let the compiler rewrite it, the simple answer is because sometimes, you don't want operator-> to return T*, where operator* returns T&. Allowing them to be overloaded separately allows combinations that you can't necessarily think of. But it would be silly to disallow it, just because we can't currently think of any reason to change it. Much like, you could have an int128 class and overload operator+= to mean exponentiation instead, if you so wanted.

DeadMG
I agree with the idea that forbidding something just because you can't think of a good reason to allow it is bad, so +1, but then I think any argument supporting the overloading of `->` would also support the overloading of `.`, which C++ does not not allow you to overload.
j_random_hacker
Not necessarily. Remember that at least one operator must remain non-overloadable, so that people can still access the base class at all times.
DeadMG
I don't follow sorry -- why is it important to be able to always access the base class? Furthermore, it's not clear to me why `->` is more suitable for overloading than `.`.
j_random_hacker
j_random_hacker: Let's take class x and let's say it overloads two and only two operators, the `.` operator and the `->` operator. The `.` operator returns class y and the `->` operator returns class z, and let's say class y overloads operator `->`. If we call `x->member`, then the compiler translates that to `x.operator -> ()->member`, but x has overloaded operator `.` which returns y, so which operator `->` are we calling? It's this ambiguity that prevents us from overloading operator `.`, Similar ambiguities can happen with other operators, but they can happen much easier with operator `.`.
Joe D