What is the difference between value cast and reference cast? Why one of them invokes conversion (aka creating new object) and other doesn't? What are caveats of using casting on rhs?
Assume this is Derived . Why those will not actually cast to Base?
*this = (Base&) rhs
(Base)* this = rhs
Could you please show on simple examples?
Value cast creates a new value from an existing one; reference cast creates a new reference to the same existing value.
Reference cast neither changes the content of an existing object nor creates a new one; it is restricted to changing the interpretation of the value that is already there. Value casting, on the other hand, can make a new object from an existing one, so it has fewer restrictions.
For example, if you have an unsigned char and you want a value or a reference of type int, value cast is going to work, while reference casting is going to fail:
unsigned char orig = 'x';
int v(orig); // Works
int &r(orig); // Does not work
rhs is Derived, I want to assign all inherited and non-inherited stuff from rhs into Base
Then you need to cast both sides to Base, or add an assignment operator to Derived that takes a const Base& as an argument. Here is an example of casting on both sides (may be hard to understand by other programmers reading your code)
struct Base {
int x;
Base(int x) : x(x) {}
};
struct Derived1 : public Base {
Derived1(int x) : Base(x) {}
};
struct Derived2 : public Base {
Derived2(int x) : Base(x) {}
};
Running the code below
Derived1 d1(5);
Derived2 d2(10);
cout << d1.x << " " << d2.x << endl;
((Base&)d1) = (Base&)d2;
cout << d1.x << " " << d2.x << endl;
produces the following printout:
5 10
10 10
As you can see, the assignment ((Base&)d1) = (Base&)d2 copied the content of d2's Base portion into d1's Base portion (demo).
What is the difference between value cast and reference cast?
Value casts convert an object to the value:
char i = 'a';
int k = static_cast<int>(i); // Prefer C++ casts to C casts
Reference casts convert an object to a reference:
char i = 'a';
int &k = static_cast<int&>(i);
Just because the conversion can be done implicitly in int &k = i doesn't mean it doesn't happen.
Why one of them invokes conversion (aka creating new object) and other doesn't?
If you write int &x = static_cast<int&>(i), there are 2 things that can happen:
1) A pointer is created pointing to i (references are hidden pointers). Then this hidden pointer gets assigned to x, and x behaves as a reference of i.
2) Usually, the compiler optimizes away this reference, and simply considers x an alias of i. Therefore no variable is instantiated.
In the former case, a new object is created.
However, if you write:
char c = 'a';
int i = static_cast<int> (c);
there is no instantiation, just a copy of the memory from c to i.
Why those will not actually cast to Base?
*this = (Base&) rhs
You cannot assign the base object to a derived object, only the opposite. This will most likely overwrite the fields of the base object to the derived object's.
(Base)* this = rhs
There is no point in castling an l-value. This is equivalent to:
*this = rhs;
What are caveats of using casting on rhs?
I don't think there is anything wrong with casting, as long as they do not decrease readability.
Related
I am learning about inheritance in C++. And i came across the following statement:
In other words, the this pointer cannot be aliased in a constructor:
extern struct D d;
struct D
{
D(int a) : a(a), b(d.a) {} // b(a) or b(this->a) would be correct
int a, b;
};
D d = D(1); // because b(d.a) did not obtain a through this, d.b is now unspecified
The above example is from cppreference.
My first question is that it is written that "this cannot be aliased in a ctor" but then in the example above, they've written in the comment "b(this->a) would be correct". This seems to be a contradiction to me because when they said that this cannot be aliased in a ctor i understood that "this cannot be used in a ctor". But then why are they saying that writing b(this->a) would be correct if this cannot be used/aliased in a ctor. Isn't the initializer list considered "in a ctor"?
Now lets look at a custom example:
struct Name
{
private:
int x = 0;
Name(int n )
{
this->x = 4; //is the use of "this" well-defined here?
}
};
My second question is that is the use of the expression this->x inside the converting constructor shown above well-defined? I mean since according the quote at the beginning of my question this can't be aliased in a ctor, so this shouldn't be valid.
What is meant is that during the construction of a class object any access to the object's non-static data members should happen through a pointer/glvalue obtained directly or indirectly from this of the constructor. Otherwise the value read by such an access is unspecified.
So this->a is always fine, as is simply a which is implicitly the same as this->a.
It is also ok to copy the pointer this and access members through that, e.g.
auto p = this;
b = p->a;
or to store a reference to the object:
auto& r = *this;
b = r.a;
But in the example given at the beginning, d is the same object as this points to. In other words the name d and *this are aliases for the same object. It is not allowed to use this other alias d to access non-static data members of the class while it is under construction because d was not obtained from this. Or to be more precise, it is unspecified what value such an access will read. So b(d.a) may or may not initialize the b member to the same value as a.
See [class.cdtor]/2.
class myclass1 {
public:
virtual ~myclass1() {
}
};
class myclass2 : public myclass1 {
};
int main() {
myclass1 obj1;
myclass2 obj2;
myclass1 *p1 = &obj2;
myclass2 *p2 = static_cast<myclass2 *>(&obj1);
if( p1 && p2){
cout << typeid(p1).name() << endl;
cout << typeid(p2).name() << endl;
}
}
The output is as below:
P8myclass1
P8myclass2
Process finished with exit code 0
The code has two classes, I tried to use two types pointer to point to the other type. From base class to its children is totally ok while the other way around should not work ("myclass2 *p2 = static_cast<myclass2 *>(&obj1);"). If I use "dynamic_cast", the casted pointer will be null. But if I use "static_cast", the cast seems successful and the type is "myclass2" when I use typeid method.
When I am in debug mode in Clion, it seems the debugger knows the real type of the pointer, as shown in the image. It knows the type of p1 is "myclass2" and type of p2 is "myclass1". What is the magic of it?
obj1 = {myclass1}
obj2 = {myclass2}
p1 = {myclass2 * | 0x7ffeec114a08} 0x00007ffeec114a08
p2 = {myclass1 * | 0x7ffeec114a10} 0x00007ffeec114a10
typeid(p1) will give you the type of the pointer p1, which will always be myclass1 * (the string P8myclass1 is the mangled name for the type myclass1 *). If you want the type of the pointed-at object, you want typeid(*p1), which should be myclass2 in this case.
With p2, typeid(p2) will be myclass2 *, while typeid(*p2) gives undefined behavior -- you can't safely dereference a pointer after a static cast to the wrong type. It is likely that you'll get myclass1, but not certain -- you might get a crash.
The debugger is essentially doing that with extra knowledge and protection to avoid bad misbehavior from the undefined behavior.
The "magic" is likely undefined behaviour. You can't static_cast a pointer to point to a different type except in specific situations (like an upcast). In your case, &obj1 points to a myclass1 object, not a myclass2 object, so it is completely meaningless to perform the downcast.
Came across a code. I am posting the snippet.
Animal & Animal::operator=(const Animal & o) {
cout << "assignment operator" << endl;
if(this != &o) {
_type = o._type;
_name = clone_prefix + o._name;
_sound = o._sound;
}
return *this;
}
int main( int argc, char ** argv ) {
Animal a;
a.print();
const Animal b("goat", "bob", "baah");
b.print();
const Animal c = b;
c.print();
a = c;
a.print();
return 0;
My question: This is he pointer to the current object right? The assignment operator returns the object of Animal type right? since it is returning *this? But isn't it supposed to return address of object since its return type is Animal & ?
I am not understanding this.
The operator= returns an Animal&, i.e. a reference to an Animal. Maybe your confusion stems from the fact that & is also used to take the address of an object, i.e. to produce a pointer to an object. As a type modifier, it means "reference". So *this is correct.
Think about this code snippet:
Animal a{}; //suppose Animal has a default constructor
Animal& b = a;
Animal* c = &a;
Animal& d = *c;
a is the actual object, whereas b is a reference to a (not a pointer, so a goes on the right-hand side and not the address of a). c is a pointer to a, so we assign it the address of a with &a on the right-hand side. d is also a reference to a, but instead of just assigning a to it, we dereference the pointer c (completely equivalent to the second line). Imagine this instead of c and the return value of the function instead of d and you have return *this.
General note: It is a well-known idiom and a useful convention that operator= returns a reference of the assigned-to object, because it mimics the behavior of primitive types. However, chaining of assignments like
a = b = c
would also be possible if it returned by value (i.e. Animal instead of Animal&). However, this would create an extra copy (at least theoretically, if the compiler cannot optimize it away for some reason). No matter how you do it, returning by value or reference makes the above statement possible, whereas returning a pointer wouldn't. Users of your class would be really confused if the operator= returned a pointer to an Animal, i.e. an Animal*.
The following code gives the output as 136. But I could not understand how the first two address comparisons are equal. Appreciate any help to understand this. Thank you.
#include <iostream>
class A
{
public:
A() : m_i(0){ }
protected:
int m_i;
};
class B
{
public:
B() : m_d(0.0) { }
protected:
double m_d;
};
class C : public A, public B
{
public:
C() : m_c('a') { }
private:
char m_c;
};
int main( )
{
C d;
A *b1 = &d;
B *b2 = &d;
const int a = (reinterpret_cast<char *>(b1) == reinterpret_cast<char *>(&d)) ? 1 : 2;
const int b = (b2 == &d) ? 3 : 4;
const int c = (reinterpret_cast<char *>(b1) == reinterpret_cast<char *>(b2)) ? 5 : 6;
std::cout << a << b << c << std::endl;
return 0;
}
When you use multiple inheritance like in your example the first base class and the derived class share the same base address. Additional classes you inherit from are arranged in order at an offset based on the size of all preceding classes. The result of the comparison is true because the base address of d and b1 are the same.
In your case, if the size of A is 4 bytes then B will start at base address of A + 4 bytes. When you do B *b2 = &d; the compiler calculates the offset and adjusts the pointer value accordingly.
When you do b2 == &d an implicit conversion from type 'C' to type 'B' is performed on d before the comparison is done. This conversion adjusts the offset of the pointer value just as it would in an assignment.
It’s pretty typical for a derived class (like C here) to be laid out in memory so it starts with its two base classes (A and B), so the address of an instance of type C would be identical to the address of the instance its first base class (i.e. A).
In this kind of inheritance (when virtual is not involved,) each instance of C will have the following layout:
First, there will be all members of A (which is just m_i, a 4-byte integer)
Second will be all members of B (which is just m_d, an 8-byte double)
Last will be all members of C itself, which is just a character (1 byte, m_c)
When you cast a pointer to an instance of C to A, because A is the first parent, no address adjustment takes place, and the numerical value of the two pointers will be the same. This is why the first comparison evaluates to true. (Note that doing a reinterpret_cast<char *>() on a pointer never causes adjustment, so it always gives the numerical value of the pointer. Casting to void * would have the same effect and is probably safer for comparison.)
Casting a pointer to an instance of C to B will cause a pointer adjustment (by 4 bytes) which means that the numerical value of b2 will not be equal to &d. However, when you directly compare b2 and &d, the compiler automatically generates a cast for &d to B *, which will adjust the numerical value by 4 bytes. This is the reason that the second comparison also evaluates to true.
The third comparison return false because, as said before, casting a pointer to an instance of C to A or to B will have different results (casting to A * doesn't do adjustment, while casting to B * does.)
In relation to question "reference to abstract class" I wrote the following example:
#include <iostream>
#include <vector>
class data
{
public:
virtual int get_value() = 0;
};
class data_impl : public data
{
public:
data_impl( int value_ ) : value( value_ ) {}
virtual int get_value() { return value; }
private:
int value;
};
class database
{
public:
data& get( int index ) { return *v[index]; }
void add( data* d ) { v.push_back( d ); }
private:
std::vector< data* > v;
};
int main()
{
data_impl d1( 3 );
data_impl d2( 7 );
database db;
db.add( &d1 );
db.add( &d2 );
data& d = db.get( 0 );
std::cout << d.get_value() << std::endl;
d = db.get( 1 );
std::cout << d.get_value() << std::endl;
data& d_ = db.get( 1 );
std::cout << d_.get_value() << std::endl;
d_ = db.get( 0 );
std::cout << d_.get_value() << std::endl;
return 0;
}
To my surprise, the example prints:
3
3
7
7
and it looks like the reference assignment does the work differently from my expectation. I would expect:
3
7
7
3
Could you please point what my mistake is?
Thank you!
In
data& d = db.get( 0 );
std::cout << d.get_value() << std::endl;
d = db.get( 1 );
std::cout << d.get_value() << std::endl;
the first statement is a reference initialization, while the third statement is a slicing assignment.
You can not re-seat a reference.
Cheers & hth.,
The reason is, references can't be reassigned.
You assigned the reference d to the first element in db:
data& d = db.get( 0 );
Then later you tried to reassign it:
d = db.get( 1 );
However, this doesn't change the reference itself, instead it changes the value the reference points to.
However, in this case the reference is to an abstract base class which contains no data. So the assignment isn't changing anything.
In effect you print the first element in db twice, then the 2nd element again twice.
I am going to simplify the example a bit:
data_impl a(3), b(7);
data &ra(a);
data &rb(b);
std::cout << ra.get_value() << std::endl;
ra = rb; // [1]
std::cout << ra.get_value() << std::endl;
Now with that simplified code it is easier to reason about the program. You obtain a reference ra to the a subobject, but the reference is of type data, rather than data_impl. Similarly with rb and b. In the line marked with [1] you are performing an assignment from rb to ra, the static type of the two arguments of the expression is data, and that means that regardless of what the real object they refer to is, that particular line is assigning only the data subobject. This is called slicing.
That is, [1] is setting the data subobject of a to be the same as the data subobject of b. Because data does not contain any actual data, the result is that a remains unmodified.
For a more illustrative example, you can add a field to data, and check that the expression does modify that field in a, even if it does not modified the fields in derived classes.
Then you can try an implement operator= manually as a virtual function and check that you can get the expected result, but the implementation will be a bit messier, as there are no covariant arguments to member functions in C++, which means that the signature of operator= at all levels will take a (const) reference to data, and you will be forced to downcast (verify that the cast succeeded) and then perform the assignment...
The problem can be seen here:
data& d = /**/;
d = /**/; <---
Here, the static type of d is data&, meaning that it is in fact syntactic sugar for:
d.operator=(/**/);
Since data::operator= is not virtual, then it is called. No dispatch to a derived class. And since data is a pure interface, it does not have any attribute, so the code is meaningless.
The problem is, there are no real solution here:
Making operator= virtual is a mess, because the parameter must be a data const& in data, and cannot later be changed in subsequent derived classes, meaning a loss of type safety
Using a setValue/getValue virtual pair will only work if the derived classes do not hold more data than they present via this interface, which is an unreasonnable assumption
Since the problem cannot be solved, the only way out is to make diagnosis easier: disabling copy and assignment in the base class.
There are two ways to do this:
Make the whole hierarch non-copyable (either inherit from boost::noncopyable or delete the copy operator and copy assignment operator, etc...)
Make the current class non-copyable, but allow derived classes to be copyable automatically, by making the copy constructor and assignment operator protected.
In general, it is an error for a non-final class to have a public copy constructor and assignment operator (I wonder if any compiler diagnose this).
Following lines might not be what you think:
data& d = db.get( 0 );
std::cout << d.get_value() << std::endl;
d = db.get( 1 );
the third line, d does not become a reference to the second database entry. It is still a reference to the first. References can only be initialized on creation.
You can't separate the reference from the referent. enter link description here
Unlike a pointer, once a reference is bound to an object, it can not be "reseated" to another object. The reference itself isn't an object (it has no identity; taking the address of a reference gives you the address of the referent; remember: the reference is its referent).
The strangest thing is the compiler don't warn it.
I try with gcc -Wall -pedantic