6

I am trying to understand how overloading -> operator works. I have the following classes

class Message {

public:
    Message(string message) :m_text(message) {}
    void printText() {
        cout << "text is " << m_text << endl;
    }
    string m_text;
};


class MessagePointerWrapper
{
public:

    MessagePointerWrapper(string message)  {
        m_message = std::make_unique<Message>(message);
    }

    Message* operator->() {

        return m_message.get();
    }

    std::unique_ptr<Message> m_message;

};

int main(int argc, char** argv)
{

    MessagePointerWrapper messageWrapper =  MessagePointerWrapper("Hello World");
    messageWrapper.m_message->printText();
    messageWrapper->m_text = "PQR";
    messageWrapper.m_message->printText();
}

The MessageWrapper class's -> operator is overloaded to return a Message*. So in the main method when I call messageWrapper-> what it returns is a Message*. Usually when I have a pointer, I need to use -> operator or the deference operator to access the object. According to that logic, to access the m_text veriable of the Message object, the code should be written as below

(messageWrapper->)   // this returns a pointer to Message. so to access the object, I should write as
(messageWrapper->)->m_text = "PQR"

or

*(messageWrapper->).m_Text = "PQR"

but this does not work that way and I need to call it as

messageWrapper->m_text = "PQR";

I don't understand the logic here. Can I get a clarification on this please.

==============

Some further notes :

In the main method I saw the below two methods do the same thing

messageWrapper.operator->()->m_text = "JKH";

messageWrapper->m_text = "JKH";

does it mean the operator -> works different from other operators where it means

messageWrapper-> is equivalent to (messageWrapper.operator->())-> and not messageWrapper.operator->() as is the case of other operators.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
3mr
  • 312
  • 2
  • 16
  • 1
    `*(messageWrapper->).m_Text` is syntactically wrong. – Jabberwocky Dec 13 '18 at 08:33
  • Can you kindly explain why. that's what I don't understand. (messageWrapper->) is a pointer to a messge. dereferncing that pointer to access the object should be either (messageWrapper->)-> or *(messageWrapper->) – 3mr Dec 13 '18 at 08:37
  • Possible duplicate of [overloading-member-access-operators-c][1]. [1]: https://stackoverflow.com/questions/8777845/overloading-member-access-operators-c – P.W Dec 13 '18 at 09:04

4 Answers4

9

As the standard states, [over.ref]/1

An expression x->m is interpreted as (x.operator->())->m for a class object x of type T if T::operator->() exists and if the operator is selected as the best match function by the overload resolution mechanism

That means messageWrapper->m_text is a syntax sugar of (messageWrapper.operator->())->m_text. You can apply the latter style explicitly, but the former is more efficient. The overloaded operator-> makes it possible to use the class like raw pointers, and that's how smart pointers like std::unique_ptr and std::shared_ptr work.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
5

The standard says :

13.5.6 Class member access

An expression x->m is interpreted as (x.operator->())->m for a class object x of type T if T::operator->() exists and if the operator is selected as the best match function by the overload resolution mechanism

Jean-Baptiste Yunès
  • 34,548
  • 4
  • 48
  • 69
3

-> is a binary operator, it works using both arguments and will continue to resolve the left hand side so long as it is not a pointer.

That is, in the following code after the call to Wrapper2::operator ->() the compiler sees that the return type is a reference and calls Wrapper1::operator ->, only then does the call result in a pointer and 'm' is resolved against RealType.

struct RealType
{
    int m;
};

class Wrapper1 {
    RealType rt;

public:
    RealType * operator ->() { return &rt; }
};
class Wrapper2 {
    Wrapper1 w1;

public:
    Wrapper1 & operator->() { return w1; }
};

int main()
{
    Wrapper2 w;
    w->m = 1;
};
SoronelHaetir
  • 14,104
  • 1
  • 12
  • 23
2

The operator -> has to return a pointer, when it is used the return value is auto de-referenced, so you don't have to deference it by yourself adding a second ->

bruno
  • 32,421
  • 7
  • 25
  • 37