Reputation: 342
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.
Upvotes: 6
Views: 217
Reputation: 172924
As the standard states, [over.ref]/1
An expression
x->m
is interpreted as(x.operator->())->m
for a class objectx
of typeT
ifT::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.
Upvotes: 9
Reputation: 15162
-> 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;
};
Upvotes: 3
Reputation: 36401
The standard says :
13.5.6 Class member access
An expression
x->m
is interpreted as(x.operator->())->m
for a class objectx
of typeT
ifT::operator->()
exists and if the operator is selected as the best match function by the overload resolution mechanism
Upvotes: 5
Reputation: 32596
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 ->
Upvotes: 2