How do I create and use a class arrow operator? [duplicate] - c++

This question already has answers here:
What are the basic rules and idioms for operator overloading?
(8 answers)
Closed 4 months ago.
So, after researching everywhere for it, I cannot seem to find how to create a class arrow operator, i.e.,
class Someclass
{
operator-> () /* ? */
{
}
};
I just need to know how to work with it and use it appropriately.
- what are its inputs?
- what does it return?
- how do I properly declare/prototype it?

The operator -> is used to overload member access. A small example:
#include <iostream>
struct A
{
void foo() {std::cout << "Hi" << std::endl;}
};
struct B
{
A a;
A* operator->() {
return &a;
}
};
int main() {
B b;
b->foo();
}
This outputs:
Hi

The arrow operator has no inputs. Technically, it can return whatever you want, but it should return something that either is a pointer or can become a pointer through chained -> operators.
The -> operator automatically dereferences its return value before calling its argument using the built-in pointer dereference, not operator*, so you could have the following class:
class PointerToString
{
string a;
public:
class PtPtS
{
public:
PtPtS(PointerToString &s) : r(s) {}
string* operator->()
{
std::cout << "indirect arrow\n";
return &*r;
}
private:
PointerToString & r;
};
PointerToString(const string &s) : a(s) {}
PtPtS operator->()
{
std::cout << "arrow dereference\n";
return *this;
}
string &operator*()
{
std::cout << "dereference\n";
return a;
}
};
Use it like:
PointerToString ptr(string("hello"));
string::size_type size = ptr->size();
which is converted by the compiler into:
string::size_type size = (*ptr.operator->().operator->()).size();
(with as many .operator->() as necessary to return a real pointer) and should output
arrow dereference
indirect dereference
dereference
Note, however, that you can do the following:
PointerToString::PtPtS ptr2 = ptr.operator->();
run online: https://wandbox.org/permlink/Is5kPamEMUCA9nvE
From Stroupstrup:
The transformation of the object p into the pointer p.operator->() does not depend on the member m pointed to. That is the sense in which operator->() is a unary postfix operator. However, there is no new syntax introduced, so a member name is still required after the ->

class T {
public:
const memberFunction() const;
};
// forward declaration
class DullSmartReference;
class DullSmartPointer {
private:
T *m_ptr;
public:
DullSmartPointer(T *rhs) : m_ptr(rhs) {};
DullSmartReference operator*() const {
return DullSmartReference(*m_ptr);
}
T *operator->() const {
return m_ptr;
}
};
http://en.wikibooks.org/wiki/C++_Programming/Operators/Operator_Overloading#Address_of.2C_Reference.2C_and_Pointer_operators

The "arrow" operator can be overloaded by:
a->b
will be translated to
return_type my_class::operator->()

Related

C++ Why does the automatic re-application of the arrow (->) operator not apply to pointer-to-pointer types?

This question discusses how the arrow operator -> is automatically re-applied to the return value of an overloaded operator->() until the value returned is a raw pointer, at which point the raw pointer is dereferenced as if with ->. However, the same is not true when one has a pointer-to-pointer type that they wish to dereference to the base value - they must use (*ptr_to_ptr)->foo() instead. It seems to me that usage as ptr_to_ptr->foo() would be unambiguous, even more so than the automatic re-application of -> onto returned values until a raw pointer is returned. So, what was the reasoning behind this decision?
minimal working example:
#include <iostream>
struct Dog {
void bark() { std::cout << "woof!" << std::endl; }
};
struct DogWalker {
Dog* dog;
Dog* operator->() {
return dog;
}
};
struct DogOwner {
DogWalker walker = { new Dog() };
DogWalker operator->() {
return walker;
}
};
void main()
{
DogOwner owner;
owner->bark(); // works, prints "woof"
Dog** ptr_to_ptr = new Dog*;
*ptr_to_ptr = new Dog;
(**ptr_to_ptr).bark(); // works
(*ptr_to_ptr)->bark(); // works
//ptr_to_ptr->bark(); // ERROR
//C2227: left of '->bark' must point to class/struct/union/generic type
}
The language adopts much of its semantics from C. The -> operator when applied to a pointer type is only valid if the pointer points to a non-array composite type. Since C doesn't have classes, C++ defined its own semantics for the overloaded -> that made sense for the smart pointer use cases.
You can achieve the behavior you want with a helper class.
template <typename T>
struct Unwrap {
T *p_;
Unwrap (T *p = 0) : p_(p) {}
T * operator -> () const { return p_; }
};
template <typename T>
struct Unwrap<T *> {
T **p_;
Unwrap (T **p = 0) : p_(p) {}
Unwrap<T> operator -> () const { return *p_; }
};
template <typename T>
Unwrap<T> make_unwrap (T *p) { return p; }
You can then use it like this:
struct foo {
void bar () { std::cout << "Hello\n"; }
};
int main () {
foo p;
auto pp = &p;
auto ppp = &pp;
auto pppp = make_unwrap(&ppp);
pppp->bar();
}
Try it online!

Dereferencing not working for smart pointer in set

#include<iostream>
#include<set>
template <typename T>
/* Simple smart pointer class */
class SmartPtr
{
T *ptr;
public:
explicit SmartPtr(T *p = NULL) { ptr = p; }
~SmartPtr() { delete(ptr); }
T & operator * () { return *ptr; }
T * operator -> () { return ptr; }
};
class simple {
private:
int x;
public:
simple(int y = 0) :x(y) {}
int getX() { return x; }
};
typedef SmartPtr<simple> simplePtr;
int main() {
std::set<simplePtr> st;
simplePtr p1 = simplePtr(new simple(5));
simplePtr p2 = simplePtr(new simple(5));
simplePtr p3 = simplePtr(new simple(5));
simplePtr p4 = simplePtr(new simple(5));
std::cout << p1->getX(); <-- working fine
st.insert(p1);
st.insert(p2);
st.insert(p3);
st.insert(p4);
for (std::set<simplePtr>::iterator it = st.begin(); it != st.end(); ++it)
{
std::cout << it->getX(); // Not working??
}
}
Compilation is failed with error in Visual Studio 2013:
Error C2039 getX: is not a member of SmartPtr<simple>
On linux:
error: ‘const class SmartPtr<simple>’ has no member named ‘getX’
Is this a problem with iterator??
You can think of it->getX() as a syntactic sugar for (*it).getX(). [In principle, a class can overload the -> and * (dereferencing) operators inconsistently, but std::set<T>::iterator, unsurprisingly, doesn't break that convention]. So, in your case, *it is dereferenced to an lvalue of type const SmartPtr<simple>&, and the .getX() applied to it fails, because SmartPtr doesn't have a getX() method. Since, instead you mean to access the object that the obtained SmartPtr points to, you must add one more level of dereferencing:
Correction 1
Replace it->getX() with (**it).getX() or (*it)->getX().
There is still another problem, though - *it results in a const SmartPtr (yes, std::set's non-constant iterator doesn't provide write access to the container's elements, otherwise you could break correct ordering of elements in the container). But both -> and * (dereferencing) operators in SmartPtr are defined in such a way that they can be invoked only on non-const objects. To fix that, you must make those two functions const:
Correction 2 (in SmartPtr<T>)
// vvvvv
T & operator * () const { return *ptr; }
T * operator -> () const { return ptr; }
// ^^^^^
After you make this second correction, you can replace your old-style for-loop with a range-for loop:
for (const simplePtr& p : st)
{
std::cout << p->getX();
}
Still, your program will not compile - SmartPtr<T> objects cannot be put in an std::set since they are not comparable. Fix that by defining operator<():
Correction 3
Add to SmartPtr<T>:
bool operator<(const SmartPtr& other) const { return ptr < other.ptr; }
At this point your code will compile but chances are high that it will not work correctly. The reason is that the copy-semantics of SmartPtr<T> is left to compiler's discretion which fails to meet your intent. This is easy to guess by spotting the violation of the Rule of Three, Four and Five - your class defines the destructor but fails to define the copy and/or move constructor and the assignment operator. As a result your code performs double deletion and therefore cannot be guaranteed any well defined behavior.
Correction 4
Fix the copy semantics of SmartPtr<T>.
I "fixed" your code by assigning move semantics to SmartPtr (this required adding std::move() when insert()-ing it into std::set):
#include<iostream>
#include<set>
template <typename T>
class SmartPtr
{
T *ptr;
public:
explicit SmartPtr(T *p = NULL) { ptr = p; }
~SmartPtr() { delete(ptr); }
SmartPtr(const SmartPtr& other) = delete;
SmartPtr(SmartPtr&& other) : ptr(other.ptr) { other.ptr = NULL; }
SmartPtr& operator=(SmartPtr other)
{
std::swap(ptr, other.ptr);
return *this;
}
T & operator * () const { return *ptr; }
T * operator -> () const { return ptr; }
bool operator<(const SmartPtr& other) const { return ptr < other.ptr; }
};
class simple {
int x;
public:
simple(int y = 0) : x(y) {}
int getX() { return x; }
};
typedef SmartPtr<simple> simplePtr;
int main() {
std::set<simplePtr> st;
simplePtr p1 = simplePtr(new simple(5));
simplePtr p2 = simplePtr(new simple(5));
st.insert(std::move(p1));
st.insert(std::move(p2));
for (const simplePtr& p : st)
{
std::cout << p->getX();
}
return 0;
}
Your iterator needs to be dereferenced, at which point you get a pointer. Which then needs to be dereferenced. So:
std::cout << (*it)->getX();
For starters you have to define operator < for the class SmartPtr before using it in the set.
Secondly you have to declare the member function getX like
int getX() const { return x; }
And you have to write at least like
std::cout << ( *it )->getX();

Using a wrapper class as a pointer to the type it wraps?

I have a simple wrapper class for Integer types, defined like so:
class Integer {
public:
Integer() {
}
Integer(const int& value) {
this->value = value;
}
int toInt() const {
return value;
}
operator int() const {
return toInt();
}
private:
int value = 0;
};
What I'd like to do, is pass the class above to a function which has a signature like this:
doSomething(int* value)
If I were to use a normal int, I could simply do:
int value = 5;
doSomething(&value);
However, when using the wrapper class I can't since it would use a pointer to the class instead of the actual underlying value.
I know of the address operator operator&, which I could use to return a pointer to the value, but it would prevent me from getting a pointer to the class itself if I needed to.
So ideally there would be a way that would allow me to use &myclass to get a pointer to the class or the underlying value, depending on what is needed.
Is there such a way?
It seems I was able to solve my own problem.
I took some inspiration from a comment by #Arunmu and the following question: https://stackoverflow.com/a/9569120
By overloading the operator&() (address-of) operator and returning a proxy class object, which has implicit conversion operators to pointers of both the type of my original class and the value it wraps I can use the syntax I needed.
I will provide an example for anyone who encounters the same problem:
class IntegerPointer {
public:
IntegerPointer(int& value, Integer& wrapper) : value(value), wrapper(wrapper) {
}
operator int*() {
return &value;
}
operator Integer*() {
return std::addressof(wrapper);
}
private:
int& value;
Integer& wrapper;
};
class Integer {
public:
Integer() {
}
Integer(const int& value) : value(value) {
}
Integer(const Integer& value) : Integer(value.value) {
}
IntegerPointer operator&() {
return IntegerPointer(value, (*this));
}
protected:
int value;
};
This allows you to use syntax such as:
Integer test = 5;
doSomething(&test);
Where &test can be used as a pointer to the Integer object or as a pointer to the int value it wraps.

c++ about operator* overloading

Is there any possible way to overload operator* in such way that it's assigning and observing functions are defined apart?
class my_class
{
private:
int value;
public:
int& operator*(){return value;}
};
int main()
{
my_class obj;
int val = 56;
*obj = val; // assign
val = *obj; // observe, same operator* is called
}
Sort of -- you can have the operator* return an instance of another class, rather than returning a reference directly. The instance of the other class then defines both a conversion operator and an assignment operator.
(In your sample code, it looks like you've overloaded the multiplication operator when you meant to overload the dereferencing operator; I'll use the dereferencing operator below.)
For example:
class my_class
{
friend class my_class_ref;
public:
my_class_ref operator*() { return my_class_ref(this); }
private:
int value;
};
class my_class_ref
{
public:
operator int() { return owner->value; } // "observe"
my_class_ref& operator=(int new_value) { owner->value = new_value; return *this; } // "assign"
private:
my_class* owner;
my_class_ref(my_class* owner) { this->owner = owner; }
};
There are some caveats. For example, as my_class_ref is implemented with a pointer to its parent class, your code must be careful that my_class_ref always has a lifetime shorter than the lifetime of the corresponding my_class -- otherwise you will dereference an invalid pointer.
In practice, if you pretend that my_class_ref doesn't exist (i.e. never declare a variable with that class) it can work very well.
Write your class like so
class my_class
{
private:
int value;
public:
int operator*() const { // observing
return value;
}
int& operator*() { // assigning
return value;
}
};
Then these operators are dissambiguated by constness, so code like this is possible
int _tmain(int argc, _TCHAR* argv[])
{
my_class a;
*a = 1; // assigning
int k = *(const_cast<my_class const&>(a)); // observing
return 0;
}

Overloaded 'dereference' or 'member of pointer' operators don't get run when I have a pointer to an object

I have the following code:
#include <iostream>
struct Base {
int i_;
};
class El : protected Base {
public:
int get_i() const { return i_; }
void set_i(int i) { i_ = i; }
};
class It : protected Base {
public:
using pointer = const El*;
using reference = const El&;
reference operator*() const
{
return reinterpret_cast<reference>(*this);
}
pointer operator->() const
{
return reinterpret_cast<pointer>(this);
}
};
int main()
{
It it;
It* itp = &it;
std::cout << *****(itp)->get_i() << "\n"; //ERROR
}
Both GCC and Clang++ somehow fail to invoke either of operator* or operator->, so I get an error It doesn't have member function 'get_i' in the last line regardless how many indirections I try. Does the standard warrant such unintuitive behavior?
Operator precedence: -> binds more tightly, so is applied to the pointer itp.
When you overload operator->, that doesn't affect the meaning of operator-> applied to a pointer-to-your-class. You want (*itp)->get_i();, I think.