How arrow-> operator overloading works internally in c++? - c++

I understand the normal operator overloading. Compiler can translate them to method call directly. I am not very clear about the -> operator. I was writing my first custom iterator and I felt like the need of -> operator. I took a look at the stl source code and implemented my own like it:
MyClass* MyClassIterator::operator->() const
{
//m_iterator is a map<int, MyClass>::iterator in my code.
return &(m_iterator->second);
}
Then I can use an instance of MyClassIterator like:
myClassIterator->APublicMethodInMyClass().
Looks like the compiler does two steps here.
1. Call the ->() method the get a temporary MyClass* variable.
2. Call the APublicMethodInMyClass on the temp variable use its -> operator.
Is my understanding correct?

The operator-> has special semantics in the language in that, when overloaded, it reapplies itself to the result. While the rest of the operators are applied only once, operator-> will be applied by the compiler as many times as needed to get to a raw pointer and once more to access the memory referred by that pointer.
struct A { void foo(); };
struct B { A* operator->(); };
struct C { B operator->(); };
struct D { C operator->(); };
int main() {
D d;
d->foo();
}
In the previous example, in the expression d->foo() the compiler will take the object d and apply operator-> to it, which yields an object of type C, it will then reapply the operator to get an instance of B, reapply and get to A*, after which it will dereference the object and get to the pointed data.
d->foo();
// expands to:
// (*d.operator->().operator->().operator->()).foo();
// D C B A*

myClassIterator->APublicMethodInMyClass()
is nothing but the following:
myClassIterator.operator->()->APublicMethodInMyClass()
The first call to the overloaded operator-> gets you a pointer of some type which has an accessible (from your call-site) member function called APublicMethodInMyClass(). The usual function look-up rules are followed to resolve APublicMethodInMyClass(), of course, depending on whether it is a virtual or not.
There is not necessarily a temporary variable; the compiler may or may not copy the pointer returned by &(m_iterator->second). In all probability, this will be optimized away. No temporary objects of type MyClass will be created though.
The usual caveats also do apply to m_iterator -- make sure that your calls do not access an invalidated iterator (i.e. if you are using vector for example).

Related

Operator overloading behavior based on member

Struct A
{
int* ptr;
int operator+=(const A& other)
{
// if(ptr == nullptr)
// { DoSomething(); }
// else { ...}
}
...
}
Is it possible to have a template operator+= (on a NON-template Class A) in order to resolve that if statement at compile time?
EDIT: I have a bunch of API's, all with the signature:
A funcX()
{
// lot of A's overloaded operators calls
return A;
}
Now, I need to use those API's with a new type, that shares a lot of members with A and add a new one (a smart pointer).
Since all the A funcX() return by value, I cannot derive my new type from A and call A funcX() with the derived type (slicing).
So my solution is to add a smart pointer to A, and a constructor in A that initializes that pointer.
So now I get to use all the API's, and when A is constructed with its new constructor (that initializes the pointer), I want all the operators to have a different behaviour than the existing one.
I was looking into some templates tricks to avoid the cost of checking the pointer inside each operator, although from the comments it doesn't seem possible to achieve it.

Overloading operator= of user defined type with unique_ptr data member

I have a user defined class that has a std::unique_ptr member. I am trying to overload the assignment operator to new an object of the same type to the unique_ptr member or assign the value to the ptr value.
class object
{
public:
// POD types
object& operator=(const object& _obj);
std::unique_ptr<baseClass> ptr;
}
I've tried using:
std::unique_ptr::swap()
but the swap function is non-const so I attempted assigning the the result of:
std::unique_ptr::get()
to the ptr and then calling:
std::unique_ptr::release()
but release also is non const, so I cannot guarantee that ptr will be destructed correctly since the old ptr will still have ownership. Alas, operator=() for unique_ptr does not accept a non-const reference to another unique_ptr so I have opted to make the operator= as so for my class.
object& object::operator=(const object& _obj)
{
//POD types use operator= as normal
ptr.reset(nullptr);
return *this;
}
And just call a setPtr() method later on to set the ptr. I wanted to just detect the type and just assign the ptr using:
ptr.reset(new detectedType );
but the consensus all over Stack Overflow and my preferred search engine is that detecting types like this would be a design flaw. My question is: am I overlooking something simple and is there a better way to overload the assignment operator for user defined types that have a unique_ptr member?
Before post edit:
I changed the parameter to object& rather than const object& and it worked. Is this a good way to correct this issue or should I be trying to work with only const references to the other class?
My question is: am I overlooking something simple and is there a better way to overload the assignment operator for user defined types that have a unique_ptr member?
There is indeed. This breaks down to understanding what smart pointers in C++ do. Usually you use a std::unique_ptr if there will be one and only one reference to your object. This is the reason why std_unique_ptrs are neither copy-constructable nor copy-assignable. If you manually create two copies of a std::unique_ptr as you describe in you post by using ptr.get() you will end up in an invalid state, because as soon as one copy leaves scope, the std::unique_ptr destructor will also destruct the object it points to, leaving the other std::unqiue_ptr in an invalid state (it points to a freed memory location). So if there might be several references to your object you should probably use std::shared_ptr instead.
In your examples the question is what do you want to achieve?
If you want your assignment operator such that after calling a = b, a.ptr and b.ptr point to the exact same object (i.e. store the same pointer) use a shared pointer. You should definitely NOT use a unique pointer in this case, as calling the destructor for a or b would destruct the object pointed to, leaving a or b in an invalid state.
If you want that after calling a = b, a.ptr points to independent copy of *(b.ptr) you can indeed use a unique pointer and implement you assignment operator like this (provided baseClass is copy construable) :
object& object::operator=(const object& _obj)
{
//POD types use operator= as normal
ptr.reset(new baseClass(*_obj.ptr));
return *this;
}
Note however that this only works if baseClass is final (unlikely considering the name...), i.e. ptr won't store pointers to objects of a derived class. Otherwise this results in slicing as pointed out by BenVoigt.
Finally, it is generally expected that calling a = b does not change b. Therefore making the ptr member mutable and using swap sounds like a bad idea to me. If you want to achieve this behavior because of performance (or other) considerations, I would suggest to implement the move assignment operator instead
object& object::operator=(object&& _obj)
{
//POD types use operator= as normal
ptr = std::move(_obj.ptr);
return *this;
}
and call a = std::move(b), which makes clear that b might end up in a different state.
This is a good use case for the mutable keyword. If you declare ptr as mutable, you are indicating that even when an instance of your class is semantically const, it must perform non-const operations on a particular data member. Declare your class as follows, and you should be able to implement your original idea of using swap:
class object
{
public:
// POD types
object& operator=(const object& _obj);
mutable std::unique_ptr<baseClass> ptr;
}

What good is the c++'s `const` promise?

I am trying to understand c++'s const semantics more in depth but I can't fully understand what really the constness guarantee worth is.
As I see it, the constness guarantees that there will be no mutation, but consider the following (contrived) example:
#include <iostream>
#include <optional>
#include <memory>
class A {
public:
int i{0};
void foo() {
i = 42;
};
};
class B {
public:
A *a1;
A a2;
B() {
a1 = &a2;
}
void bar() const {
a1->foo();
}
};
int main() {
B b;
std::cout << b.a2.i << std::endl; // output is 0
b.bar();
std::cout << b.a2.i << std::endl; // output is 42
}
Since bar is const, one would assume that it wouldn't mutate the object b. But after its invocation b is mutated.
If I write the method foo like this
void bar() const {
a2.foo();
}
then the compiler catches it as expected.
So it seems that one can fairly easily circumvent the compiler with pointers. I guess my main question is, how or if I can be 100% sure that const methods won't cause any mutation to the objects they are invoked with? Or do I have completely false expectations about const?
Why does c++ allow invocation of non-const methods over pointers in const methods?
EDIT:
thanks to Galik's comment, I now found this:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4372.html
Well, this was exactly what I was looking for! Thanks!
And I find Yakk's answer also very helpful, so I'll accept his answer.
const tells the caller "this shouldn't mutate the object".
const helps the implementor with some errors where accidentally mutating state generates errors unless the implementor casts it away.
const data (not references, actual data) provides guarantees to the compiler that anyone who modifies this data is doing undefined behaviour; thus, the compiler is free to assume that the data is never modified.
const in the std library makes certain guarantees about thread safety.
All of these are uses of const.
If an object isn't const, anyone is free to const_cast away const on a reference to the object and modify it.
If an object is const, compilers will not reliably diagnose you casting away const, and generating undefined behavior.
If you mark data as mutable, even if it is also marked as const it won't be.
The guarantees that the std provides based off const are limited by the types you in turn pass into std following those guarantees.
const doesn't enforce much on the programmer. It simply tries to help.
No language can make a hostlie programmer friendly; const in C++ doesn't try. Instead, it tries to make it easier to write const-correct code than to write const-incorrect code.
Constness by itself doesn't guarantee you anything. It only takes away rights of specific code to mutate an object through a specific reference. It doesn't take away rights of other code to mutate the same object through other references, right under your feet.
So it seems that one can fairly easily circumvent the compiler with pointers.
That is indeed true.
I guess my main question is, how or if I can be 100% sure that const methods won't cause any mutation to the objects they are invoked with?
The language guarantees that only in a local sense.
Your class is, indirectly, the same as the following:
struct Foo
{
int* ptr;
Foo() : ptr(new int(0)) {};
void bar() const { *ptr = 10; }
};
When you use:
Foo f;
f.bar();
the member variable of f did not change since the pointer still points to the location after the call to f.bar() as it did before the call. In that sense, f did not change. But if you extend the "state" of f to include the value of what f.ptr points to, then the state of f did change. The language does not guarantee against such changes.
It's our job, as designers and developers, to document the "const" semantics of the types we create and provide functions that preserve those semantics.
Or do I have completely false expectations about const?
Perhaps.
When a class method is declared as const, that means the implicit this pointer inside the method is pointing at a const object and thus the method cannot alter any of the members of that object (unless they are explicitly declared as mutable, which is not the case in this example).
The B constructor is not declared as const, so this is of type B*, ie a pointer to a non-const B object. So a1 and a2 are non-const, and a1 is declared as a pointer to a non-const A object, so the compiler allows a1 to be pointed at a2.
bar() is declared as const, so this is of type const B*, ie a pointer to a const B object.
When bar() calls a1->foo(), a1 is const by virtue of this pointing at a const B object, so bar() can't change a1 to point at something else. But the A object that a1 is pointing at is still deemed to be non-const by virtue of a1's declaration, and foo() is not declared as const, so the compiler allows the call. However, the compiler can't validate that a1 is actually pointing at a2, a member of B that is supposed to be const inside of bar(), so the code breaks the const contract and has undefined behavior.
When bar() tries to call a2.foo() instead, a2 is const by virtue of this pointing at a const B object, but foo() is not declared as const, so the compiler fails the call.
const is just a safety catch for well-behaving code. It does not stop you from shooting yourself in the foot by using misbehaving code.
This is a correct observation. In const-qualified functions (bar in your example) all data members of the class are behaving as if they are const data members when accessed from this function. With pointers, it means that the pointer itself is constant, but not the object it points to. As a matter of fact, your example can be very much simplified into:
int k = 56;
int* const i = &k;
*i = 42;
There is a big difference between pointer to constant object and constant pointer, and one needs to understand it, so that 'promises', which were not given in the first place, would not seem to be broken.

C++ Arithmetic Operator without new return object

This is a rather simple concept. I have an arithmetic operator and I wish for the operation to update not create an object. Essentially,
MyType A;
MyType B;
MyType C;
A = B - C;
A = C - B;
The statement A = B - C should not call the constructor for MyType again since A is already created, i.e. the constructor should only be called 3 times in this program. Is there some way to achieve this while maintaining a nice syntax? I suspect that I could let the operator function know not to create an object but rather to use some reference. Is there a more streamlined way to do this using some c++ syntax unknown to me?
class MyType
{
MyType* OperatorReturnObj;
MyType operator-(const MyType& Left, const MyType& Right)
{
// This removed and replaced with SetOperatorReturnObj function
// MyType OperatorReturnObj();
OperatorReturnObj= Left - Right;
return OperatorReturnObj;
}
void MyType SetOperatorReturnObj(MyType* Ref)
{
OperatorReturnObj = Ref;
}
};
Without ugly tricks like creating pool of objects and using them internally (where complications would be more expensive than any benefits for this case), there is no way to achieve this. Problem is that statement:
A = B - C;
is equal to:
A.operator=( operator-( B, C ) );
as you can see operator-( B, C ) has to be executed first and provide result somewhere. So you either have to change syntax like:
A += B; A -= C; // or even
(A += B) -= C; // or maybe
A += B -= C;
or use regular syntax and if you need efficiency implement move semantics.
After some thought you can create object of type MyTypeExression which does not do actual calculations but remember arguments and operations and only do them later when MyType::operator=(MyTypeExression) is executed. Though for that to work you probably need a wrapper with smart pointer for MyType so MyTypeExression does not have to copy it's arguments. So this solution can be considered as complicated trick I mentioned before.
I wish for the operation to update not create an object
For A = B - C;, A will be updated, by operator=.
More particular, operator- 's return value is passed by value, a temporary variable has to be created inside operator- and then copied/moved to A.
BTW1: OperatorReturnObj= Left - Right; will cause an infinite recursion.
BTW2: MyType A(); is a function declaration indeed. Most vexing parse
You cannot.
In A = B - C, first the B - C expression is evaluated. Then the result is copied to A.
The B - C expression knows nothing about A, so it is not possible to update A directly.
You can overload the assignment operator but this will not prevent the creation of a new object.
Some tips:
First of all, you didn't specified how to initialize the internal pointer attribute, so at this moment it points to an unspecified value in the memory stack.
Second, the operator- method(!) isn't defined like you did. look at "binary arithmetic operators"
binary operators have to return a reference to the object they are called from.
Third, the object itself is passed "automagically" to its methods, and its available as the this pointer.
So you don't have to pass it as the first parameter.
Finally, to return a reference you have to return the value, and not the pointer : --> *this.
So an expression like A=B+C is evaluated to:
A.operator=(B.operator+(C))
So B+C is different than C+B. (I don't enter in the details of inheritance here..)
Inspired from the same previous link above:
class X {
int internalprivatevalue;
//....
public:
X& operator+(const X& second) {
// actual addition of second to *this takes place here
// e.g. internalprivatevalue+=second. internalprivatevalue;
return *this; // return the result by reference!!
}
}
Hope to have clarified a little more your doubts.

Change 'this' pointer of an object to point different object

class C{
//methods and properties
}
void C::some_method(C* b){
delete this;
this = b;
}
This gives me follwing error when compiling:
error: lvalue required as left operand of assignment
My intention:
Say there are objects a and b of class C. The contents of the class C can be very huge and field by field copying can be very costly. I want all the contents of 'a' to be replaced by 'b' in an economical way.
Will default copy constructor do the intended task?
I found something called 'move constructor'
http://akrzemi1.wordpress.com/2011/08/11/move-constructor/
Perhaps, it might get the effect that I want.
The this-pointer is an implicit pointer to the object in whose context you are working, you cannot reassign it.
According to Stroustrup's bible (The C++ Programming Language, 3rd edition I have) this is expressed as
C * const this
meaning you have a constant pointer to your class C, so the compiler will complain if you try to change it.
EDIT:
As I was corrected, the above mentioned expression does not describe this fully correctly, for this is actually an rvalue.
You cannot change what this points to. I would also not know why you'd want to do this.
To quote the standard:
In the body of a non-static (9.3) member function, the keyword
this is a prvalue expression whose value is the address of the
object for which the function is called.
A "prvalue" is a pure rvalue, something like 42 or 3.14159.
In the same way you can't do something like 42 = x, you can't
assign to this; in both cases (at least conceptually), there
is no object whose value can change.
And I'm really curious as to what you expect to happen if
I write something like:
int
main()
{
C c1;
C c2
c1.some_method( &c2 );
}
Do you expect the address of c1 to somehow miraculously
change, and for c1 and c2 to be aliases to the same object?
(And c1.some_method( NULL ) is even more intreguing.)
You can't assign a different value to this as it point to the object itself, and this haven't any sense.
You could instantiate a new object and use its implicit this pointer instead.
Moreover, if you try to make a copy of object, you can overwrite operator=
class Foo
{
public:
[...]
Foo& operator=(const Foo& foo);
}
int main()
{
Foo foo;
foobar = foo; //invoke operator= overwrited method
}
The error says "You can't assign b to this". As far as I know, this is something you can't change, because it's not an actual pointer, but a self-reference.
Just use the usual approach instead of black magic and UB:
C* c1 = new C();
C* c2 = new C();
// do some work perhaps ...
delete c1;
c1 = c2;
Now c1 is an alias to c2 as you wanted. Be careful though when cleaning up memory so you don't end up deleting an object twice. You might perhaps consider smart pointers...