Thread: operator-> and operator*

  1. #1
    Registered User
    Join Date
    Dec 2004
    Location
    UK
    Posts
    109

    operator-> and operator*

    I'm wondering why operator->() returns a raw poiter to an object when operator*() returns a refence

    a->b is supposed to be synctactic sugar for (*a).b and is apparently implemented as:
    Code:
    (*(a.operator->()).b
    
    or
    
    (a.operator->())->b
    where the return of operator->() has to be a raw pointer

    wouldn't an implementation as:
    Code:
    (a.operator->()).b
    whit operator->() returning a reference been more logical? Or even implement it as a macro for (*a).b so that only one dereference operator exists? (while having 2 might be useful for operator overloading, the automatic functionality tacked on to operator-> limits its possible applications)

  2. #2
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Operator -> and * are not similar, so there is no reason for their return types to be in any way similar.

    -> is not "implemented as" anything. It's built into the compiler. Anything else wouldn't make sense.

    By the way, the requirements on the return type of operator -> are merely "something that operator -> can be applied to". As such you can return another object that overloads ->, and that object's -> will be called also. The chain continues until an operator returns a raw pointer.

    One important thing: in C++, (*x).member and x->member are no longer the same! A programmer is encouraged to make them behave the same, but he is not, in any way, required to do so.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  3. #3
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by sigfriedmcwild
    I'm wondering why operator->() returns a raw poiter to an object when operator*() returns a refence
    Because that's the way those operators are defined by the standard.

    That definition was inherited from C, in which a->b (with a being a pointer) is a shortcut for (*a).b. The quantity (*a) is sometimes described as a reference, and is a different thing from a pointer.

    Quote Originally Posted by sigfriedmcwild
    a->b is supposed to be synctactic sugar for (*a).b and is apparently implemented as:
    Code:
    (*(a.operator->()).b
    
    or
    
    (a.operator->())->b
    where the return of operator->() has to be a raw pointer
    That's not true. If we ignore the potential that a C++ class can provide an operator->() and operator*() members, and go back to method by which a C compiler implements those operators, both a->b and (*a).b are more usually implemented by compilers in terms of machine instructions that manipulate raw memory. It is much more efficient that way.

    Quote Originally Posted by sigfriedmcwild
    wouldn't an implementation as:
    Code:
    (a.operator->()).b
    whit operator->() returning a reference been more logical?
    Only if you're a Java programmer, and believe that everything in life is a reference. The reason I make that comment is that questions like yours most commonly come from Java programmers (although once I saw it asked by a C# programmer) who are trying to translate some existing Java code into C++, and scratching heads over why some of their usages of the form "a.b" in Java have to be translated to a->b in C++ but some usages do not.

    operator->() operates on a pointer to a struct or class, while operator*() operates on a specific object (nominally described as a reference) of that struct type.

    Quite a few programming languages only support references, and there is never a pointer in sight. Java is one of those; I presume C# is another. But, a fair few "bridging tutorials" from those languages give the message that a pointer in C++ is just a reference in a different syntactic form. So the first question of a lot of people working through those tutorials is "why not make them the same?" The truth, however, is not that simple. Pointers are different things from references. It just happens that in some circumstances that a pointer can be obtained from a reference or vice versa.
    Quote Originally Posted by sigfriedmcwild
    Or even implement it as a macro for (*a).b so that only one dereference operator exists? (while having 2 might be useful for operator overloading, the automatic functionality tacked on to operator-> limits its possible applications)
    As CornedBee implied, the truth is actually the reverse of that. It is a common convention in C++ that operator->() and operator*() for a class be implemented so they appear related, but there is no technical requirement that they actually be related.

  4. #4
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Java's (or C#'s) references are not the same beast as C++'s references. They're sort of midway between references and pointers: they can be null, they can be modified to point to a different object. However, they can never point at raw memory and you cannot perform any pointer arithmetic on them.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  5. #5
    Registered User
    Join Date
    Dec 2004
    Location
    UK
    Posts
    109
    My question was about why the standard was written with those 2 operators, which are very similar (at least for built in types) defined so differently.

    And Java "references" are not references in any shape or form. Java only supports passing arguments by value. What are called "references" are in fact crude and simple pointers (just like the C/C++ ones, except you can't do arithmetic operations on them)

  6. #6
    Its hard... But im here swgh's Avatar
    Join Date
    Apr 2005
    Location
    England
    Posts
    1,688
    In general, ( what I was told ) you use the indirection operator when using pointers with claasses and maaging objects on the heap. The other operator, ( the dot ) is used when working on objects that are not related to pointers. This is way I have looked at it. If I am working with pointers in a class, or managing heap memory with new and delete, I always use the indirection operator, as it refrers it "points to" the object in question, much like the THIS pointer.

    Code:
    return this->*m_age;

  7. #7
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by sigfriedmcwild
    My question was about why the standard was written with those 2 operators, which are very similar (at least for built in types) defined so differently.
    The C language as originally specified in (roughly; I'm uncertain of the exact date) the late 1970's and early 1980's introduced the feature because typing (*a).element was both finger intensive (two of the characters are shifted), error prone (leaving out the braces changed the meaning of the expression due to language precedence rules and the fact that this expression uses two operators), and somewhat difficult for a programmer to read (meaning it was more difficult to maintain code). Typing a->b is less finger intensive (less total typing, only the greater-than sign is shifted), less error prone (-> is one operator rather than two) and arguably easier for the programmer to read. For these reasons, -> became quite widely used in C, before there was ever a C standard. Both operator * (dereference of a pointer) and operator . (member of struct) were also quite widely used in expressions with no pointer to a struct in sight, so could not be removed without breaking the language. Standards committees for programming languages are rather reluctant to remove features from a language that are in common usage (as operator these three operators have been since the early days of C) as doing so would break a considerable amount of code and discourage programmers from using the language and compiler vendors from implementing it. Therefore all three of these operators were enshrined in the first C standard (first ratified by ANSI in 1989 and then adopted by ISO in 1990).

    One of the requirements of the C++ standard was backward compatibility to the 1989 C standard. So all three operators survived into the C++ standard, and were extended in various ways.
    Quote Originally Posted by sigfriedmcwild
    And Java "references" are not references in any shape or form.
    From the perspective of the Java language, Java references are indeed references --- they just happen to have some properties that references in other programming languages do not share. As CornedBee said, a Java reference is (conceptually) a bit like a hybrid of a C++ pointer and a C++ reference.

    Quote Originally Posted by sigfriedmcwild
    Java only supports passing arguments by value. What are called "references" are in fact crude and simple pointers (just like the C/C++ ones, except you can't do arithmetic operations on them)
    CornedBee has already addressed the distinction between Java references and C++ references/pointers quite well.

    Java only supports passing arguments by value. However, Java references can be passed by value --- this makes implementing basic things like a function which can swap its arguments pretty difficult in Java.

  8. #8
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Quote Originally Posted by sigfriedmcwild
    My question was about why the standard was written with those 2 operators, which are very similar (at least for built in types) defined so differently.
    But they are not even remotely similar!

    * is an unary operator "dereferences a pointer", i.e. it provides value-level access to the pointee of any indirect reference. It is easily defined as an operator that takes the indirect reference as an argument (usually the this argument) and returns the pointee.

    -> is a binary operator, with the left-hand argument being an indirect reference and the right-hand argument being an identifier, i.e. something that has no runtime C++ representation, which provides access to members of the pointee of the indirect reference. It cannot be defined by normal C++ language rules: there is no construct that can be passed as the right-hand side, because it has to be resolved at compile time.
    As such, the language designers (I'm not sure if Bjarne Stroustrup himself allowed overloading of -> or if the committee did) had to find a way to express the semantics of operator -> in a way that made sense, not in one that directly reflects the way the operator works.
    The designers, after evaluating the various possibilities, decided to use the way it is now.


    If you want a more detailed explanation, you could ask on Usenet, in either comp.lang.c++.moderated or in comp.std.c++
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

Popular pages Recent additions subscribe to a feed