overloading the operator -> - c++

I was looking at overloading the -> operator. I came up with the following simple example:
struct foo
{
int p = 12;
};
struct A
{
foo* f;
A()
{
this->f = new foo();
}
foo* operator-> ()
{
return f;
}
};
int main()
{
A a;
std::cout << a->p; //output 12
}
Although this example works, yet I am hoping if someone could tell me why it works.
I was thinking that it should work like this
std::cout << a->->p; //The first arrow returns the pointer
//the second arrow dereferences it
but here it seems like a single arrow not only returns the pointer but it also dereferences it. Is this a special case in C++?

Yes, this is by-design, operator-> will be called recursively on the return value; then we can use such class (so-called smart pointers) in the same way as raw pointers.
If a user-defined operator-> is provided, the operator-> is called again on the value that it returns, recursively, until an operator-> is reached that returns a plain pointer. After that, built-in semantics are applied to that pointer.

Related

Syntax using the return value from an overloaded -> operator

So I think I might be overthinking this but I wanted to know if someone could clarify why the following statement works in the given code
f->hello();
This is the code
struct bar
{
void hello()
{
std::cout << "Hello World";
}
};
struct foo
{
bar* f;
foo() {
f = new bar();
}
~foo() {
delete f;
}
bar* operator->() {
return f;
}
};
int main()
{
foo f;
f->hello(); //Works
}
Since the following code from above returns a pointer
bar* operator->() {
return f;
}
should'nt
f->hello(); actually be f->->hello();
why does f->hello() work and f->->hello() fails ?
The reason i am thinking that f->->hello() should work is because
f->ptrReturned->hello();
If we overload the -> operator are we required to return a ptr type ? Form what I have tried it seems returning an object type is not allowed
Your understanding is correct basically, but ->-> is not valid syntax. You can use the overloaded operator-> like
f.operator->()->hello();
//^^^^^^^^^^^^ return the pointer
// ^^^^^^^^^ call hello() on the returned pointer
f->hello() is treated as the same of f.operator->()->hello(); that makes the usage of class with overloaded operator-> (e.g. smart pointers) consistent with built-in pointers. The usage would be more natural, and could be used with raw pointers in more general context like templates.
And,
If we overload the -> operator are we required to return a ptr type ?
There're some restrictions:
The overload of operator -> must either return a raw pointer, or return an object (by reference or by value) for which operator -> is in turn overloaded.
Apparently, that's just how an overloaded operator-> works. The part before the -> (not including the operator itself) gets replaced with the return value of the overloaded operator. For example, std::unique_ptr::operator-> returns a pointer, which then gets dereferenced by the ->.

C++ pointer and address clarification in relation to the current object

So far to my understanding, when defining a pointer variable, we are allocating space in RAM for that variable.
int *p;
Would define a space in RAM. Then we assign a memory address to that pointer using `&variable'.
I'm looking over at an example on: *this vs this in C++
The code is:
#include <iostream>
class Foo
{
public:
Foo()
{
this->value = 0;
}
Foo get_copy()
{
return *this;
}
Foo& get_copy_as_reference()
{
return *this;
}
Foo* get_pointer()
{
return this;
}
void increment()
{
this->value++;
}
void print_value()
{
std::cout << this->value << std::endl;
}
private:
int value;
};
int main()
{
Foo foo;
foo.increment();
foo.print_value();
foo.get_copy().increment();
foo.print_value();
foo.get_copy_as_reference().increment();
foo.print_value();
foo.get_pointer()->increment();
foo.print_value();
return 0;
}
I don't understand what the purpose of putting the * operator in front Foo* get_copy() and Foo* get_pointer() does. Why do I get an error if I removed the * from the Foo* functions while returning this not *this?
edit:
Also, why is:
foo.get_copy().increment();
foo.print_value();
yielding 1 not 2?
I don't understand what the purpose of putting the * operator in front Foo* get_copy() and Foo* get_pointer() does
Foo* get_pointer()
Foo* is a pointer that points to Foo object.
this is also a pointer that implicitly bind to the calling object of the member function. That's why the return type of those functions are Foo* not Foo.
The * is part of the type. So int means type int, and
int* type pointer to int. If the function returns pointer
to int, it is int* foo(), and if it retu rns a pointer to
Foo, it is Foo* Foo::get_pointer().
The definition reserves space for the defined object. A
declaration doesn't reserve any space, and definitions of things
that aren't objects (e.g. references or functions) don't reserve
any space either, at least not that you can see. (Obviously, a
function does exist somewhere in memory, and in many cases, the
compiler will need space as well for its implementation of a
reference. But they are invisible within the scope of C++.)
this, is always a pointer in C++, though you don't mention it explicitly anywhere. So while returning a this pointer, should use Foo*
this is actually an implicit object passed during function call, which is a pointer to the object which calls the function
It seems that you have changed the code from the example that you refer to so that get_copy() no longer returns a copy.
There are 2 ways of using * in your code example. One is for type declaration and the other is the dereferencing operator.
First the type declarations:
int *p means declaring p as a variable of type "pointer to an int".
Foo *get_pointer() means that the function get_pointer will return a value of type "pointer to a Foo object".
Now the dereferencing:
*p means "the value that p points to".
int a = 42;
int *p; // p is of type "pointer to an int"
p = &a; // set p to the address of a (p now "points to" a)
a = 117; // change the value of a
int x = *p; // set x to the value that p points to (which is a) - x will be 117
this is just a pointer to the object. *this means "the object that this points to". In your example this is of type Foo* (pointer to a Foo object) while *this is of type Foo (a Foo object).
"this" is a pointer.
you want to return a pointer to the instance (a specific allocated object).
Foo* get_pointer(){
return this;
}
or you want to return a pointer to the copy, allocate a new object.
//need to implement the copy here
Foo* get_copy(){
return this;
}
and not to the reference (address of the instance). this is why you need to return the pointer.

std::shared_ptr or std::unique_ptr assignment operator overloads

I don't see a reason why these don't have an assignment operator overload for plain old pointers of the type they're templated to. If the goal of making smart pointers interface as close to plain old pointers as they could, then why didn't they make an overload for the assignment operator like this?
inline std::shared_ptr<type> &operator=( const type * pointer)
{
reset(a);
}
this way you could use them just like you would a normal pointer, like so:
std::shared_ptr<int> test = new int;
it's not an issue at all, just wondering why they went to the trouble of just overloading a couple of operators.
Also wondering if there's a way to overload the global assignment operator to do this, or if there's any reason i shouldn't.
edit: adding a response to Nawaz about his answer here for code formatting. I just wrote this test program to see if what you were saying was right:
template<class T>
class peh
{
public:
peh() {meh = 3;}
const peh<T> & operator=(const int * peh)
{
}
};
void f( peh<int> teh)
{
}
int main()
{
int * meh = new int;
f(meh);
system("PAUSE");
return 0;
}
this here errors out saying there is no usable conversion from peh<int> to int *. so why is it acceptable with std::shared_ptr<int> to int *?
Also wondering if there's a way to overload the global assignment operator to do this, or if there's any reason i shouldn't.
No. Assignment operator overload must a member function.
By the way, if you want the following functionality, then you should not talk about assignment operator, you should rather ask : why the constructor which takes raw pointer as argument is made explicit? why it is not implicit?
//this code requires an implicit constructor, not assignment!
std::shared_ptr<int> test = new int; //illegal
It is illegal, but suppose for a while that this was allowed, then you would be able to call the following function passing a raw pointer as argument : such a feature would be dangerous, as the rest of the answer explains (read the comments) :
void f(std::shared_ptr<int> test)
{
//code
} //test will be destructed here (when it goes out of scope)
//if test.use_count() == 1, then the pointer which it manages
//will be destructed as well. (NOTE THIS POINT)
Now see the dangerous part:
int *ptr = new int;
f(ptr);
//note that calling f is allowed if it is allowed:
//std::shared_ptr<int> test = new int;
//it is as if ptr is assigned to the parameter:
//std::shared_ptr<int> test = ptr;
//Question : now what happened in f()?
//Answer : inside f(), test (the shared_ptr) will infer that no one else
//refers to the pointer it contains, because test.use_count() == 1
//test is obviously wrong in this case, because it cannot prove that!
//DANGER
*ptr = 10; //undefined behavior, because ptr is deleted by the shared_ptr
Please read the comments. It explains each part of the code snippet above.
The operator= you show would not actually enabled the syntax you want. shared_ptr<int> p = new int; would use shared_ptr's constructor from T* and shared_ptr's copy constructor. shared_ptr has both of these, but your syntax does not work because the constructor from T* is explicit.
The reason for this is because if that construction, std::shared_ptr<int> test = new int;, could be done implicitly it would mean that a shared_ptr could take ownership of a pointer without anyone ever explicitly asking it to. Nawaz shows one reason this would be really error prone; you'd have to be really careful that a pointer you're using isn't suddenly adopted by a shared_ptr somewhere without your knowledge, and then destroyed out from under you.
Here's an example that shows this dangerous implicit construction:
#include <iostream>
template<typename T>
struct owning_pointer {
T *t;
owning_pointer(T *t) : t{t} {}
~owning_pointer() {
std::cout << t << " deleted\n";
delete t;
}
};
void foo(owning_pointer<int> thief) {}
int main() {
int *i = new int;
std::cout << i << " allocated\n";
foo(i);
}
The output will be something like:
0x10d400880 allocated
0x10d400880 deleted
And see the error you get when you add explicit to owning_ptr's constructor. I get:
main.cpp:18:5: error: no matching function for call to 'foo'
foo(i);
^~~
main.cpp:13:6: note: candidate function not viable: no known conversion from 'int *' to 'owning_pointer<int>' for 1st argument;
void foo(owning_pointer<int> thief) {}
^
Also it's unnecessary to allow implicit construction from T* since there are already some perfectly simple ways to allocate without the same potential for errors:
std::shared_ptr<int> test(new int); // one extra character isn't a hardship. I typically prefer () construction anyway.
std::shared_ptr<int> test{new int}; // although I might start preferring {} construction in C++11
auto test = std::make_shared<int>(); // this is slightly different in that the allocated int is zero-initialized
If you're initializing a member shared_ptr then you can initialize it in the initializer list instead of using assignment or reset() in the body of the constructor:
struct foo {
std::shared_ptr<int> m_meh;
foo()
: m_meh(new int)
{
// no need for m_meh.reset(new int) here
}
};
What operator= would enable is this:
shared_ptr<int> s;
s = new int;
This doesn't seem quite as error prone as implicit construction of shared_ptr from T*, but I can't see that there's really any value to it either.

Why does operator-> overloading not work in VC2010?

class A
{
public:
A* operator->() const
{
}
void Test() {}
};
then call it like this.
A* a = new A;
a->Test();
The code builds and runs successfully in VC2010. It seems very strange. I am wondering it is by design or it is a bug of VC2010?
Thanks
You aren't calling your operator-> in your example, you are calling Test directly from an A*. You want:
(*a)->Test();
Or...
A a;
a->Test();
There's nothing wrong with VS2010 concerning operator-> (that I know of).
Using -> on a pointer named a effectively executes: (*a).. Using -> on a variable by value will invoke your operator-> if present, or be a syntax error if there is no operator->.
Your code is:
A* a = new A;
a->Test();
The "a" is a pointer to an A. It is NOT an A object itself, it is a memory address of the A object on the heap.
When you call a->Test() you invoke the pointer's -> operator (built in for all pointer types in C++). You would have to do this to invoke your operator:
//Create an A (NOT a pointer).
A a;
//Invoke the A's operator ->
a->Test();
Which is how STL iterators work - they're class types, not pointers to class types. Note that the return type of operator -> must make sense for the operation/member you're trying to invoke.
So, here's an example that would call test via the ->:
#include <iostream>
class A
{
public:
A* operator->()
{
return this;
}
void Test() { std::cout << "Hello World!"; }
};
int main()
{
A a;
a->Test();
}
It's strange, but it works because a->Test(); returns the current object, which Test() is then called on (see the return this; line).
See STL iterators for useful examples of why you'd actually want to do this.

What are the pointer-to-member operators ->* and .* in C++?

Yes, I've seen this question and this FAQ, but I still don't understand what ->* and .* mean in C++.
Those pages provide information about the operators (such as overloading), but don't seem to explain well what they are.
What are ->* and .* in C++, and when do you need to use them as compared to -> and .?
I hope this example will clear things for you
//we have a class
struct X
{
void f() {}
void g() {}
};
typedef void (X::*pointer)();
//ok, let's take a pointer and assign f to it.
pointer somePointer = &X::f;
//now I want to call somePointer. But for that, I need an object
X x;
//now I call the member function on x like this
(x.*somePointer)(); //will call x.f()
//now, suppose x is not an object but a pointer to object
X* px = new X;
//I want to call the memfun pointer on px. I use ->*
(px ->* somePointer)(); //will call px->f();
Now, you can't use x.somePointer(), or px->somePointer() because there is no such member in class X. For that the special member function pointer call syntax is used... just try a few examples yourself ,you'll get used to it
EDIT: By the way, it gets weird for virtual member functions pointers.
For member variables:
struct Foo {
int a;
int b;
};
int main ()
{
Foo foo;
int (Foo :: * ptr);
ptr = & Foo :: a;
foo .*ptr = 123; // foo.a = 123;
ptr = & Foo :: b;
foo .*ptr = 234; // foo.b = 234;
}
Member functions are almost the same.
struct Foo {
int a ();
int b ();
};
int main ()
{
Foo foo;
int (Foo :: * ptr) ();
ptr = & Foo :: a;
(foo .*ptr) (); // foo.a ();
ptr = & Foo :: b;
(foo .*ptr) (); // foo.b ();
}
In a nutshell: You use -> and . if you know what member you want to access. And you use ->* and .* if you don't know what member you want to access.
Example with a simple intrusive list
template<typename ItemType>
struct List {
List(ItemType *head, ItemType * ItemType::*nextMemPointer)
:m_head(head), m_nextMemPointer(nextMemPointer) { }
void addHead(ItemType *item) {
(item ->* m_nextMemPointer) = m_head;
m_head = item;
}
private:
ItemType *m_head;
// this stores the member pointer denoting the
// "next" pointer of an item
ItemType * ItemType::*m_nextMemPointer;
};
When you have a normal pointer (to an object or a basic type), you would use * to dereference it:
int a;
int* b = a;
*b = 5; // we use *b to dereference b, to access the thing it points to
Conceptually, we're doing the same thing with a member function pointer:
class SomeClass
{
public: void func() {}
};
// typedefs make function pointers much easier.
// this is a pointer to a member function of SomeClass, which takes no parameters and returns void
typedef void (SomeClass::*memfunc)();
memfunc myPointer = &SomeClass::func;
SomeClass foo;
// to call func(), we could do:
foo.func();
// to call func() using our pointer, we need to dereference the pointer:
foo.*myPointer();
// this is conceptually just: foo . *myPointer ();
// likewise with a pointer to the object itself:
SomeClass* p = new SomeClass;
// normal call func()
p->func();
// calling func() by dereferencing our pointer:
p->*myPointer();
// this is conceptually just: p -> *myPointer ();
I hope that helps explain the concept. We're effectively dereferencing our pointer to the member function. It's a little more complicated than that -- it's not an absolute pointer to a function in memory, but just an offset, which is applied to foo or p above. But conceptually, we're dereferencing it, much like we would dereference a normal object pointer.
So called "pointers" to members in C++ are more like offsets, internally. You need both such a member "pointer", and an object, to reference the member in the object. But member "pointers" are used with pointer syntax, hence the name.
There are two ways you can have an object at hand: you have a reference to the object, or you have a pointer to the object.
For the reference, use .* to combine it with a member pointer, and for the pointer, use ->* to combine it with a member pointer.
However, as a rule, don't use member pointers if you can avoid it.
They obey pretty counter-intuitive rules, and they make it possible to circumvent protected access without any explicit casting, that is, inadvertently…
Cheers & hth.,
You cannot dereference pointer to members as normal pointers — because member functions require this pointer, and you have to pass it somehow. So, you need to use these two operators, with object on one side, and pointer on another, e.g. (object.*ptr)().
Consider using function and bind (std:: or boost::, depending on whether you write C++03 or 0x) instead of those, though.
Pointer-to-member access operators: .* and ->*
The pointer-to-member access operators, .* and ->*, are for dereferencing a pointer to member in combination with an object and a pointer to object, respectively. This description applies to both pointers to data members and pointers to member functions.
For example, consider the class Foo:
struct Foo {
int i;
void f();
};
If you declare a member pointer, iPtr, to an int data member of Foo:
int Foo::* iPtr;
You can initialize this member pointer iPtr so that it points to the Foo::i member:
iPtr = &Foo::i;
To dereference this pointer, you need to use it in conjunction with a Foo object.
Consider now the object foo and the pointer to object fooPtr:
Foo foo;
Foo* fooPtr = &foo;
Then, you can dereference iPtr in combination with foo or fooPtr:
foo.*iPtr = 0;
fooPtr->*iPtr = 0;
Analogously, you can use .* and ->* with pointers to function members. Note however that you will need to enclose them between parentheses because the function call operator, i.e., (), has higher precedence than both .* and ->*:
void (Foo::*memFuncPtr)() = &Foo::f;
(foo.*memFuncPtr)();
(fooPtr->*memFuncPtr)();
To conclude: you need an object to dereference a pointer to a member, and which one you use, either .* or ->* for dereferencing the pointer to member, depends on whether this needed object is directly provided or through an object pointer.
C++17 — Using std::invoke() instead
The use of both operators can be replaced since C++17 by the std::invoke function template. std::invoke provides a unified way of dereferencing member pointers regardless of whether you use them in combination with an object or an object pointer, and also regardless of whether the pointer to member corresponds to a pointer to data member or pointer to member function:
// dereference a pointer to a data member
std::invoke(iPtr, foo) = 0; // with an object
std::invoke(iPtr, fooPtr) = 0; // with an object pointer
// dereference a pointer to a member function
std::invoke(memFuncPtr, foo); // with an object
std::invoke(memFuncPtr, fooPtr); // with an object pointer
This unified syntax corresponds to the ordinary function call syntax, and it may make it easier to write generic code.