443,983 Members | 1,597 Online Need help? Post your question and get tips & solutions from a community of 443,983 IT Pros & Developers. It's quick & easy.

 P: n/a Hi, I read about operator overloading and have a question regarding "operator->()". If I have two classes like this: struct A{ void f(); }; struct B{ A* p; A* operator->(){return p;} }; Then we can do: B b; b->f(); and A's f() is called. However, the overloading of -seems to say it's a unary operator, and is prefixed (because of the ()). Therefore, to me, it looks like we should use this operator like: B::operator->b, which will give use a the pointer member in B. Then to access "f", I think we need to do: (B::operator->b)->f(), which is very different to b->f(). Can someone please tell me why using b->f() gives me the f() of A? Thanks, Jess May 4 '07 #1
14 Replies

 P: n/a Jess wrote: Hi, I read about operator overloading and have a question regarding "operator->()". If I have two classes like this: struct A{ void f(); }; struct B{ A* p; A* operator->(){return p;} }; Then we can do: B b; b->f(); and A's f() is called. However, the overloading of -seems to say it's a unary operator, and is prefixed (because of the ()). Therefore, to me, it looks like we should use this operator like: B::operator->b, No, it's shorthand for b.operator->(). which will give use a the pointer member in B. Then to access "f", I think we need to do: (B::operator->b)->f(), b.operator->()->f() -- Ian Collins. May 4 '07 #2

 P: n/a On May 4, 1:37 pm, Ian Collins (). b.operator->()->f() But the declaration A* operator->() shows "->" should be used as a prefix of "b", isn't it? Using it as a prefix should give us "->b"... Moreover, your expansion above seems to say we can expand "b" to "b.operator->()", I really can't see where it's from.... Jess May 4 '07 #3

 P: n/a Jess wrote: On May 4, 1:37 pm, Ian Collins No, it's shorthand for b.operator->(). b.operator->()->f() But the declaration A* operator->() shows "->" should be used as a prefix of "b", isn't it? Using it as a prefix should give us "->b"... How so? Operator -is a unary *postfix* operator. See section 11.10 of Stroustrup. -- Ian Collins. May 4 '07 #4

 P: n/a On May 4, 3:04 pm, Ian Collins (void) to show it has no right argument? Why do we have () in "A* operator- >()"? What does the "()" mean? b. why is b->f() the shorthand of (b.operator->()) -f() now there are two "->". I think in b->f(), "b->" should be expanded to "b.operator->". Then b->f() is the shorthand of (b.operator->)f() (which obviously isn't a valid syntax...) Thanks, Jess May 4 '07 #5

 P: n/a Jess wrote: On May 4, 3:04 pm, Ian Collins How so? Operator -is a unary *postfix* operator. I see, so we can't change its postfix property when overloading it. My other two questions are: a. if we use it as postfix, then why don't we overload it as: A* operator->(void) Why should we? Are you thinking of the prefix and postfix ++ and -- operators? Th use of (int) with this is required to disambiguate the operator. to show it has no right argument? Why do we have () in "A* operator- >()"? What does the "()" mean? b. why is b->f() the shorthand of (b.operator->()) -f() now there are two "->". I think in b->f(), "b->" should be expanded to "b.operator->". Then b->f() is the shorthand of What? b.operator->() returns a pointer: A* p = b.operator->(); a->f(); -- Ian Collins. May 4 '07 #6

 P: n/a On May 4, 8:16 am, Jess (void) to show it has no right argument? That's how we do overload it. Except that the void isn't necessary. But it has nothing to do with whether the argument is right or left. In general, when overloading operators, the first operand of the function is the first operand of the operator, and so on. If the overloaded operator is a member (required for operator->), its first operand is this, so it has one less formal parameter. Why do we have () in "A* operator- ()"? What does the "()" mean? That you are declaring/defining a function. An overloaded operator is always a function. b. why is b->f() the shorthand of (b.operator->()) -f() Because that's what the standard says. now there are two "->". I think in b->f(), "b->" should be expanded to "b.operator->". The name of the function? That doesn't make any sense. Then b->f() is the shorthand of (b.operator->)f() (which obviously isn't a valid syntax...) No, because a user defined overload is always implemented as a function, and when the operator is used, that function is called. -- James Kanze (GABI Software) email:ja*********@gmail.com Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34 May 4 '07 #7

 P: n/a "Jess" How so? Operator -is a unary *postfix* operator. I see, so we can't change its postfix property when overloading it. My other two questions are: a. if we use it as postfix, then why don't we overload it as: A* operator->(void) to show it has no right argument? Why do we have () in "A* operator- >>()"? What does the "()" mean? You seem to assume that how operator overloading is expressed in any way reflects the orientation (prefix/postfix/infix) of the operator itself. Assumptions are the mother of all fuck-ups :). Your assumption is incorrect, it only reflects the arity of the operator. An operator with 1 parameter is an unary operator (whether they are prefix or postfix), and an operator with 2 parameters is a binary operator. Keep in mind the 'this' pointer is an implicit first parameter. The only two exceptions to this are the postfix ++ and -- operators. Since both the postfix and prefix variants are unary, their function definitions would be the same and therefore ambiguous. That's why an extra int parameter is used for the postfix ++ and -- operators to distinguish them from the prefix ones. The operator syntax is just like ordinary functions, where the function name is followed by parameters contained in parantheses. The function-style name for the -operator is "operator ->". Just as you would do b.func(), you can also do b.operator->(). Likewise, ++b is the same as b.operator++(). - Sylvester Hesp May 4 '07 #8

 P: n/a On May 4, 4:44 pm, Ian Collins () returns a pointer: A* p = b.operator->(); a->f(); Yes, from here, it shows "(b.operator->()) -f()" is what we want, and I couldn't understand why it's equivalent to "b->f()". But if it is the standard (as James said), then I'll take it as a fact. :) Thanks, Jess May 6 '07 #9

 P: n/a On May 4, 7:32 pm, James Kanze >" and "<<" are used by iostream, are then member operators of iostream or non-member functions? b. why is b->f() the shorthand of (b.operator->()) -f() Because that's what the standard says. I see, it's the standard, though it doesn't look very obvious why standard says it. :) When "->" is used with a pointer "p->b()" is equivalent to (*p).b(). which doesn't show how we can use "->" with user-defined class types. Thanks, Jess May 6 '07 #10

 P: n/a On May 4, 7:53 pm, "Sylvester Hesp" ". Just as you would do b.func(), you can also do b.operator->(). Likewise, ++b is the same as b.operator++(). Thanks a lot for the explanations! Jess May 6 '07 #11

 P: n/a On 2007-05-06 13:22, Jess wrote: On May 4, 7:32 pm, James Kanze You can't change the syntax of C++ with operator overloading.You can only extend it to support additional types. By "extending", I guess I can't change a postfix to a prefix, or its precedence? Correct. Can I change a member function to a non-member and vice versa? No, but for most(?) operators there's both a member and a non-member version. Can I change a unary operator to a binary or a binary to a unary? No. Also, when ">>" and "<<" are used by iostream, are then member operators of iostream or non-member functions? Non-member, and that is how you should make the if you ever want to add an operator<< to your class so that you can easily print it. As far as I know the only operator that you have some freedom with is the ()-operator, with which you can decide how many argument it takes, all the others are quite fixed, in arity, precedence, postfix/prefix, etc. b. why is b->f() the shorthand of (b.operator->()) -f() Because that's what the standard says. I see, it's the standard, though it doesn't look very obvious why standard says it. :) When "->" is used with a pointer "p->b()" is equivalent to (*p).b(). which doesn't show how we can use "->" with user-defined class types. The idea is that you should be able to make a class that acts just like a pointer, and that requires that it works like that, take a look at this code and compare my Pointer-class with a real pointer: #include #include template class Pointer { T* ptr_; public: Pointer(T* p) : ptr_(p) {} T* operator->() { return ptr_; } T& operator*() { return *ptr_; } }; int main() { std::cout << "Using Pointer:\n"; Pointerappend(" World\n"); std::cout << *P; std::cout << "Using a std::string pointer:\n"; std::string* p = new std::string(); *p = "Hello"; p->append(" World\n"); std::cout << *p; } -- Erik Wikström May 6 '07 #12

 P: n/a Thanks a lot for the example! Jess On May 6, 9:59 pm, Erik Wikström >" and "<<" are used by iostream, are then member operators of iostream or non-member functions? Non-member, and that is how you should make the if you ever want to add an operator<< to your class so that you can easily print it. As far as I know the only operator that you have some freedom with is the ()-operator, with which you can decide how many argument it takes, all the others are quite fixed, in arity, precedence, postfix/prefix, etc. b. why is b->f() the shorthand of (b.operator->()) -f() Because that's what the standard says. I see, it's the standard, though it doesn't look very obvious why standard says it. :) When "->" is used with a pointer "p->b()" is equivalent to (*p).b(). which doesn't show how we can use "->" with user-defined class types. The idea is that you should be able to make a class that acts just like a pointer, and that requires that it works like that, take a look at this code and compare my Pointer-class with a real pointer: #include #include template class Pointer { T* ptr_; public: Pointer(T* p) : ptr_(p) {} T* operator->() { return ptr_; } T& operator*() { return *ptr_; } }; int main() { std::cout << "Using Pointer:\n"; Pointerappend(" World\n"); std::cout << *P; std::cout << "Using a std::string pointer:\n"; std::string* p = new std::string(); *p = "Hello"; p->append(" World\n"); std::cout << *p; } -- Erik Wikström May 6 '07 #13

 P: n/a On May 6, 1:22 pm, Jess >" and "<<" are used by iostream, are then member operators of iostream or non-member functions? Either (or in this case, both). >and << are binary operators; there are two ways of overloading a binary operator (with a few exceptions): as a member function taking a single argument (with the object the member function is called on as the first argument to the operator, and the function argument as the second), or as a free function taking two arguments. The important thing to keep in mind here is that when the compiler sees << or >>, it doesn't immediately consider overloading of any sort. It just notes that it has a binary operator of such and such precedance and binding, and continues building the expression tree. Only when it has finished, and is actually generating code, will it look at the possible overloads, and do overload resolution over the set of built-in operators, member function redefines, and free function redefines. b. why is b->f() the shorthand of (b.operator->()) -f() Because that's what the standard says. I see, it's the standard, though it doesn't look very obvious why standard says it. :) True, but not for the reasons you seem to be saying. A priori, looking at it, one would expect -to be a binary operator. It's not treaded as such, however, but as a unary operator. And as a unary operator, if it is defined as a member function, the operand is the object it is called on. There aren't many examples of postfix unary operators to draw on. (++ and -- are very special, as they can be either prefix or postfix.) But this behavior is consistent with the idea that a member function is called on an object, and that if the member function is a user defined operator overload, the object the function is called on is the first operand of the operator, regardless of where the operand occurs relative to the operator. When "->" is used with a pointer "p->b()" is equivalent to (*p).b(). which doesn't show how we can use "->" with user-defined class types. This is true in general. For built in operators, a += b is the equivalent of a = a + b, but there is no relationship if the operators are defined by the user (unless the user decides to create one---which is highly recommended). -- James Kanze (Gabi Software) email: ja*********@gmail.com Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34 May 6 '07 #14

 P: n/a On May 6, 1:59 pm, Erik Wikström >" and "<<" are used by iostream, are then member operators of iostream or non-member functions? Non-member, and that is how you should make the if you ever want to add an operator<< to your class so that you can easily print it. Both, or either, depending on your point of view. An operator<< which takes an std::ostream as its first operand can be either a non-member, or a member of std::ostream. The operator<< for most of the built in types are members of std::ostream. For a user defined type, of course, you're not allowed to add functions to std::ostream, so you must define it as a non-member function. As far as I know the only operator that you have some freedom with is the ()-operator, with which you can decide how many argument it takes, all the others are quite fixed, in arity, precedence, postfix/prefix, etc. Of course, the built-in () operator also accepts a variable number of arguments, so this freedom isn't "extending" the language in any way. -- James Kanze (Gabi Software) email: ja*********@gmail.com Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34 May 6 '07 #15

### This discussion thread is closed

Replies have been disabled for this discussion. 