I have a question regarding the arrow operator's overload process. The overloading makes it seem like it is a unary operator, while the usage makes it appear as a binary operator. The return value isn't intuitive either. Only a raw pointer is returned.
#include <iostream>
template <typename T>
class Auto_ptr1
{
T* m_ptr {};
public:
// Pass in a pointer to "own" via the constructor
Auto_ptr1(T* ptr=nullptr)
:m_ptr(ptr)
{
}
// The destructor will make sure it gets deallocated
~Auto_ptr1()
{
delete m_ptr;
}
// Overload dereference and operator-> so we can use Auto_ptr1 like m_ptr.
T& operator*() const { return *m_ptr; }
T* operator->() const { return m_ptr; }
};
// A sample class to prove the above works
class Resource
{
public:
Resource() { std::cout << "Resource acquired\n"; }
~Resource() { std::cout << "Resource destroyed\n"; }
void sayHi() { std::cout << "Hi!\n"; }
};
void someFunction()
{
Auto_ptr1<Resource> ptr(new Resource()); // ptr now owns the Resource
int x;
std::cout << "Enter an integer: ";
std::cin >> x;
if (x == 0)
return; // the function returns early
// do stuff with ptr here
ptr->sayHi();
}
int main()
{
someFunction();
return 0;
}
In the example ptr->sayHi(), one might expect the entire expression ptr-> to be replaced with the raw pointer, but apparently, only ptr is replaced. Anything that follows is coherent with intuition. The intermediate step is what bugs me. Is the overloading process of the arrow operator an exceptional case?
It's just a theory question.
I have a question regarding the arrow operator's overload process. The overloading makes it seem like it is a unary operator, while the usage makes it appear as a binary operator. The return value isn't intuitive either. Only a raw pointer is returned.
#include <iostream>
template <typename T>
class Auto_ptr1
{
T* m_ptr {};
public:
// Pass in a pointer to "own" via the constructor
Auto_ptr1(T* ptr=nullptr)
:m_ptr(ptr)
{
}
// The destructor will make sure it gets deallocated
~Auto_ptr1()
{
delete m_ptr;
}
// Overload dereference and operator-> so we can use Auto_ptr1 like m_ptr.
T& operator*() const { return *m_ptr; }
T* operator->() const { return m_ptr; }
};
// A sample class to prove the above works
class Resource
{
public:
Resource() { std::cout << "Resource acquired\n"; }
~Resource() { std::cout << "Resource destroyed\n"; }
void sayHi() { std::cout << "Hi!\n"; }
};
void someFunction()
{
Auto_ptr1<Resource> ptr(new Resource()); // ptr now owns the Resource
int x;
std::cout << "Enter an integer: ";
std::cin >> x;
if (x == 0)
return; // the function returns early
// do stuff with ptr here
ptr->sayHi();
}
int main()
{
someFunction();
return 0;
}
In the example ptr->sayHi(), one might expect the entire expression ptr-> to be replaced with the raw pointer, but apparently, only ptr is replaced. Anything that follows is coherent with intuition. The intermediate step is what bugs me. Is the overloading process of the arrow operator an exceptional case?
It's just a theory question.
Share Improve this question asked Jan 8 at 9:04 user29104083user29104083 112 bronze badges New contributor user29104083 is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct. 8 | Show 3 more comments2 Answers
Reset to default 2Yes, the arrow operator is an exceptional case of sorts. This is because the built-in ->
operator is itself an exceptional case. For all other binary operators, the arguments are expressions that designate objects, so x ⊕ y
means the same as x.operator⊕(y)
(⊕ is any non-exceptional binary operator).
In x->y
however, y
is not an object, but an identifier that designates a class member. One cannot pass such a thing as a parameter to a user-defined function, x.operator->(y)
would make no sense. So x->y
is made to mean x.operator->()->y
instead, in order for x
to behave just like a pointer. As a consequence, the overloaded operator->
is required to be unary, not binary.
You can show that it is a unary operator by explicitly calling it:
#include <iostream>
class MyClass
{
public:
int A = 0;
int* operator->() { return &A; };
};
int main()
{
MyClass object;
object.A = 10;
std::cout << "object.A: " << object.A << "\n";
// Explicitly call the -> operator, and de-reference the int pointer that is returned
std::cout << "object->: " << *object.operator->() << "\n";
return 0;
}
Keep in mind that returning a class member is not the normal 'expected' use of a -> operator, this is just for demonstration purposes.
->
). So I don't see a real issue with your code (other than the fact that std::unique_ptr already exists). – Pepijn Kramer Commented Jan 8 at 9:08ptr->sayHi()
. Instead it's equivalent toptr.operator->()->sayHi()
. In other words, theoperator->()
function is called on theptr
object (which isn't a pointer), it returns a pointer to aResource
object, and the pointer is dereferenced to get the actualResource
object and thesayHi()
function is called on thatResource
object. – Some programmer dude Commented Jan 8 at 9:08->
it is considered a unary operator because what appears on the right hand side is not an expression (technically it's an identifier expression, rather than a regular expression). An operator is binary if it combines two regular expressions. – john Commented Jan 8 at 9:43operator->
has special semantics"). Also related: Why overloaded de-reference operator doesn't work the same way as the arrow operator? – JaMiT Commented Jan 8 at 10:21