I have a question regarding std::move function. Please refer to the code below:
#include <iostream>
#include <memory>
using namespace std;
class CPointer {
public:
CPointer(double a, double b)
{
m_dX = a;
m_dY = b;
}
~CPointer()
{
m_dX = m_dY = 0;
}
double getX() const
{return m_dX;}
double getY() const
{return m_dY;}
private:
double m_dX;
double m_dY;
};
class CContainer
{
public:
CContainer(CPointer* p)
{
m_p = p;
}
~CContainer()
{
m_p = nullptr;
}
CPointer* getP() const
{return m_p;}
private:
CPointer* m_p;
};
class CBigContainer
{
public:
CBigContainer(CContainer* p)
{
m_p = p;
}
~CBigContainer()
{
m_p = nullptr;
}
CContainer* getP() const
{return m_p;}
private:
CContainer* m_p;
};
int main()
{
CPointer* pPointer = new CPointer(150,360);
cout << "1.) " << pPointer->getX() << " , " << pPointer->getY() << "\n";
std::shared_ptr<CContainer> spContainer = std::make_shared<CContainer>(pPointer);
cout << "2.) " << pPointer->getX() << " , " << pPointer->getY() << "\n";
std::shared_ptr<CBigContainer> spBigContainer = std::make_shared<CBigContainer>(std::move(spContainer.get())); //<--- std::move here
cout << "3.) " << spBigContainer->getP()->getP()->getX() << " , " << spBigContainer->getP()->getP()->getY() << "\n";
cout << "4.) " << spContainer->getP()->getX() << " , " << spContainer->getP()->getY() << "\n";
cout << "5.) " << pPointer->getX() << " , " << pPointer->getY() << "\n";
return 0;
}
And this is the result :
My question is , I am using a std::move
std::shared_ptr<CBigContainer> spBigContainer = std::make_shared<CBigContainer>(std::move(spContainer.get()));
So I am expecting the spContainer cannot be used after the line of code, because the object inside smart pointer is removed.
But it still work fine. It seems like has no different with not using std::move in this case.
Can you explain to me in details ?
Thank you very much.
So I am expecting the spContainer cannot be used after the line of code, because the object inside smart pointer is removed.
Your code never actually requests any move operations. The smart pointers are a red herring, you could see the same behaviour in this case:
CContainer a(pPointer);
CBigContainer b( std::move(&a) );
The last line is the same as CBigContainer b( &a ); because the constructor of CBigContainer accepts a pointer, and the behaviour of move operations for primitive types (which includes pointers) is to leave the source unchanged.
Your code makes the CBigContainer object point to the CContainer object (the latter still being managed by a smart pointer). Which is a bad idea because if you then release the CContainer smart pointer, then the CBigContainer's pointer to it will dangle.
Your CContainer and CBigContainer objects hold raw pointers to other objects. Putting those objects in smart pointers doesn't change that.
In case you are unclear, these are two different things:
Move out of a smart pointer.
Move out of the object managed by a smart pointer.
The first one will leave the smart pointer empty. The second one leaves the smart pointer active and managing an object which is in a post-move state .
Here's an example of code that will move out of the spContainer:
std::shared_ptr<CContainer> other = std::move(spContainer);
This invokes a move operation because the shared_ptr on the left has a move-constructor
which accepts another shared_ptr of the same type as argument.
Related
class Guitars
{
private:
int serialNumber{0};
float price{0.0};
// GuitarSpecs spec{};
public:
Guitars(int serNum, float price)
{
this->serialNumber = serNum;
this->price = price;
};
Guitars(const Guitars &s)
: serialNumber{s.serialNumber}, price{s.price}
{
std::cout << "Copy" << std::endl;
};
Guitars(Guitars &&source) noexcept : serialNumber{source.serialNumber}, price{source.price}
{
source.serialNumber = NULL;
source.price = NULL;
std::cout << "Move" << std::endl;
};
int GetSerial() const { return serialNumber; };
float GetPrice() const { return price; };
void SetPrice(float x) { this->price = x; }
};
class Inventory
{
private:
list<Guitars *> *guitarList;
public:
Inventory()
{
guitarList = new list<Guitars *>;
}
void AddGuitar(int serNum, float price)
{
Guitars *x = new Guitars(serNum, price);
// Guitars x(serNum,price);
guitarList->push_back(x);
}
void Display()
{
for (auto &&i : *guitarList)
{
std::cout << i->GetPrice() << " " << i->GetSerial() << endl;
}
}
~Inventory()
{
for (auto &&i : *guitarList)
{
std::cout << i->GetSerial() << " "
<< "deleted " << std::endl;
delete i;
}
std::cout << "List is deleted" << std::endl;
delete guitarList;
}
};
int main()
{
Inventory I;
I.AddGuitar(12050, 50.23);
I.AddGuitar(10000, 20.00);
I.Display();
return 0;
}
Can someone please explain to me why is the copy constructor not called in the code above?
When I created a list of Guitar pointers on the heap along with Guitar object on the heap with a pointer pointing to them and save those pointers in Inventory list, copy constructor is not called. Why is that happening and is this one more efficient since the program is not ganna create copies of object, its created once on the heap and we save the pointer with us.
Some detailed answer, based on you talking about optimization:
Your have the following code:
list<Guitars *> *guitarList;
void AddGuitar(int serNum, float price)
{
Guitars *x = new Guitars(serNum, price);
// Guitars x(serNum,price);
guitarList->push_back(x);
}
I think your reason to use all of those pointers is to be faster. If a Guitars object would be created on the stack as usual and then be pushed back, that would create a copy, true.
What you could do instead would be to define a move operation for Guitars and move the stack object into what you create in the list, like by calling the move constructor.
But even better would be to use std::list::emplace_back, like this:
list<Guitars> guitarList;
void AddGuitar(int serNum, float price)
{
guitarList.emplace_back(serNum, price);
}
In any case, if you talk about optimality, those pointers are not good. A pointer requires additional space, and every time the data is accessed, the pointer must be dereferenced. Also, as #PaulMcKenzie wrote in the comments, this can block the compiler from optimizing for you.
Also, making a list member itself a pointer, that is going with list<Guitars*>* guitarList; or list<Guitars>* guitarList;, is also not a good idea. The only reason I see is if you want to exchange the lists of two Inventory objects, but in that case, simply call std::swap on the lists.
If you drop the pointers, note how instantly every other code of yours becomes far easier. You don't even have to define your destructor at all.
(As for the actual question you asked, like #Jarod42 already wrote, copying pointers does not copy objects.)
(By the way, if the class Guitars represents a single guitar, then I'd go for the singular, Guitar.)
Edit:
I created a small series of tests with different ways to fill the list, using Guitars mostly unmodified. (I removed the assignments of the non-pointers to NULL though.) In any way, I did the following test setup:
#include <iostream>
#include <list>
class Guitar
{
private:
int serialNumber{0};
float price{0.0};
public:
Guitar(int serNum, float price)
{
std::cout << "Base" << std::endl;
this->serialNumber = serNum;
this->price = price;
}
Guitar(const Guitar& s)
: serialNumber{s.serialNumber}, price{s.price}
{
std::cout << "Copy" << std::endl;
}
Guitar(Guitar&& source) noexcept : serialNumber{source.serialNumber}, price{source.price}
{
std::cout << "Move" << std::endl;
}
};
void test_1()
{
std::cout << "test 1" << std::endl;
std::list<Guitar*> guitarList;
Guitar* x = new Guitar(1, 2.);
guitarList.push_back(x);
std::cout << std::endl;
}
void test_2()
{
std::cout << "test 2" << std::endl;
std::list<Guitar> guitarList;
Guitar x(1, 2.);
guitarList.push_back(x);
std::cout << std::endl;
}
void test_3()
{
std::cout << "test 3" << std::endl;
std::list<Guitar> guitarList;
guitarList.push_back(Guitar(1, 2.));
std::cout << std::endl;
}
void test_4()
{
std::cout << "test 4" << std::endl;
std::list<Guitar> guitarList;
guitarList.emplace_back(1, 2.);
std::cout << std::endl;
}
int main()
{
test_1();
test_2();
test_3();
test_4();
}
The output of this is:
test 1
Base
test 2
Base
Copy
test 3
Base
Move
test 4
Base
I hope this increases further understanding about how things work here.
The tests can be found under http://www.cpp.sh/35ld6
Also, I wanted to mention, if we talk about optimization, we'd have to talk about what we optimize. Right now, we have small lists of objects with almost no content. In that case, one would not optimize at all, as we talk about nanoseconds in difference.
The cases to think about are:
A small list of big objects that are easy to move. In that case, we need to make sure that no copy constructor is called, but move would be fine.
A small list of big objects that are hard to move. In that case, we only want to use the base operator, possibly by pointers as you initially did - but emplace_back also works and makes things easier. Note that the objects being hard to move would hint at a bad design for the class.
A big list of small objects. Here we want to use as few constructors as possible, including move constructors. We also don't want to use a list of pointers, as that would give us additional 64 bits per object, and a lot of derefencing later on. In that case, emplace_back really shines.
So in other words, you can't go wrong with emplace_back.
#include <iostream>
#include <vector>
using namespace std;
struct A {
int i = 0;
};
void append(vector<A>& v) {
auto a = v.back(); // is a allocated on the stack? Will it be cleaned after append() returns?
++a.i;
v.push_back(a);
}
void run() {
vector<A> v{};
v.push_back(A{}); // is A{} created on the stack? Will it be cleaned after run() returns?
append(v);
for (auto& a : v) {
cout << a.i << endl;
}
}
int main() {
run();
return 0;
}
The code above prints as expected:
0
1
But I have two questions:
is A{} created on the stack? Will it be cleaned after run() returns?
is a allocated on the stack? Will it be cleaned after append() returns?
Update:
#include <iostream>
#include <vector>
using namespace std;
struct A {
int i = 0;
A() { cout << "+++Constructor invoked." << endl; }
A(const A& a) { cout << "Copy constructor invoked." << endl; }
A& operator=(const A& a) {
cout << "Copy assignment operator invoked." << endl;
return *this;
};
A(A&& a) { cout << "Move constructor invoked." << endl; }
A& operator=(A&& a) {
cout << "Move assignment operator invoked." << endl;
return *this;
}
~A() { cout << "---Destructor invoked." << endl; }
};
void append(vector<A>& v) {
cout << "before v.back()" << endl;
auto a = v.back();
++a.i;
cout << "before v.push_back()" << endl;
v.push_back(a);
cout << "after v.push_back()" << endl;
}
void run() {
vector<A> v{};
v.push_back(A{});
cout << "entering append" << endl;
append(v);
cout << "exited append" << endl;
for (auto& a : v) {
cout << a.i << endl;
}
}
int main() {
run();
return 0;
}
Output:
+++Constructor invoked.
Move constructor invoked.
---Destructor invoked.
entering append
before v.back()
Copy constructor invoked.
before v.push_back()
Copy constructor invoked.
Copy constructor invoked.
---Destructor invoked.
after v.push_back()
---Destructor invoked.
exited append
0
0 // I understand why it outputs 0 here. I omitted the actual work in my copy/move constructors overloads.
---Destructor invoked.
---Destructor invoked.
I updated the code in my question, adding the copy/move constructors. I found copy constructor was called 3 times in append. I understand auto a = v.back(); needs a copy, But the two other copies maybe should be avoided?
The C++ specification doesn't actually say.
With v.push_back(A{}) the A{} part creates a temporary object, which is then moved or copied into the vector, and then the temporary object is discarded.
Same with local variables, really, the "stack" is actually never mentioned by the C++ standard, it only tells how life-time should be handled. That a compiler might use a "stack" is an implementation detail.
With that said, most C++ compilers will use the "stack" to store local variables. Like for example the variable a in the append function. As for the temporary object created for v.push_back(A{}) you need to check the generated assembly code.
For the life-times, the life-time of the temporary object A{} ends as soon as the push_back function returns. And the life-time of a in the append function ends when the append function returns.
In this function
void append(vector<A>& v) {
auto a = v.back(); // is a allocated on the stack? Will it be cleaned after append() returns?
++a.i;
v.push_back(a);
}
the variable a has the automatic storage duration and is a local variable of the function. It will not be alive after exiting the function.
In this function
void run() {
vector<A> v{};
v.push_back(A{}); // is A{} created on the stack? Will it be cleaned after run() returns?
append(v);
for (auto& a : v) {
cout << a.i << endl;
}
}
again the variable v has the automatic storage duration and is a local variable of the function. When the function will finish its execution the variable will be destroyed. And all elements of the vector (that are placed in the heap) also will be destroyed due to the destructor of the vector.
Consider the following demonstrative program.
#include <iostream>
#include <vector>
struct A {
int i = 0;
};
int main()
{
std::vector<A> v;
std::cout << "&v = " << &v << "\n\n";
A a;
std::cout << "&a = " << &a << "\n\n";
v.push_back( a );
std::cout << "&v = " << &v << '\n';
std::cout << "&a = " << &a << '\n';
std::cout << "&v[0] = " << &v[0] << "\n\n";
++a.i;
v.push_back( a );
std::cout << "&v = " << &v << '\n';
std::cout << "&a = " << &a << '\n';
std::cout << "&v[0] = " << &v[0] << '\n';
std::cout << "&v[1] = " << &v[1] << "\n\n";
return 0;
}
Its output might look like
&v = 0x7ffc27288dd0
&a = 0x7ffc27288dcc
&v = 0x7ffc27288dd0
&a = 0x7ffc27288dcc
&v[0] = 0x55725232ee80
&v = 0x7ffc27288dd0
&a = 0x7ffc27288dcc
&v[0] = 0x55725232eea0
&v[1] = 0x55725232eea4
As you can see the addresses of the vector v and the object a looks similarly because they are allocated in the same outer block scope of the function and have the automatic storage duration.
&v = 0x7ffc27288dd0
&a = 0x7ffc27288dcc
And they are not changed when new values are pushed on the vector.
However the addresses of the elements of the vector as for example
&v[0] = 0x55725232ee80
&v[0] = 0x55725232eea0
&v[1] = 0x55725232eea4
have a different representation and can be changed when a new elements are added to the vector because the memory for them can be dynamically reallocated.
EDIT: After you updated your question then take into account that when a new element is added to the vector the elements of the vector can be reallocated calling the copy constructor. You can use the method reserve to reserve enough memory to avoid its reallocation and the method emplace_back.
is a allocated on the stack?
There is no such thing as "stack" storage in the language. a has automatic storage.
As far as language implementations are concerned, this typically means that the variable is probably stored in a register, or on stack, or nowhere.
Will it be cleaned after append() returns?
Yes. Automatic variables are destroyed automatically when they go out of scope.
is A{} created on the stack?
A{} is a temporary object. The language is a bit vague about the storage class of temporary objects, but it is clear about the lifetime.
Will it be cleaned after run() returns?
In this case, the temporary object is destroyed at the end of the full expression, which is before run returns.
vector elements allocated on stack?
No. Vector elements are created in dynamic storage.
Update
But the two other copies maybe should be avoided?
If your endgoal is to get a vector with two elements, you can avoid all of the copies like this:
std::vector<A> v(2);
I'm having a trouble manipulating a pointer inside a class.
#include <iostream>
using namespace std;
class myClass {
public:
void modifyPointer(float *pointer);
};
void myClass::modifyPointer(float *pointer) {
pointer = new float[3];
pointer[0] = 0;
pointer[1] = 1;
pointer[2] = 2;
cout << "Address: " << pointer << endl;
}
int main()
{
float* pointer;
myClass object;
object.modifyPointer(pointer);
cout << "Address: " << pointer << endl;
cout << "Values: " << pointer[0] << "," << pointer[1] << "," << pointer[2]
<< std::endl;
return 0;
}
When I print the pointer inside the class I get its address, but in the main program I get a 0 address and a seg fault when printing values. What am I wrong in how to modify this pointer?
You are taking the pointer by copy, so you don't see the changes in main. You need to take the pointer by reference, like this:
void myClass::modifyPointer(float * &pointer) {
pointer = new float[3];
// ...
}
You have to change the declaration of this function as well, of course.
Here's a working demo.
Pointers are just like any other objects. Passing an object as an argument by value creates a copy of that object. This means the original pointer in main() remains unaffected. And since it is unaffected and not initialized, accessing an array out of bounds with pointer[any_index] results in undefined behavior. Pass by reference instead:
void modifyPointer(float*& pointer)
I am trying to learn C++ and from what I've read in books and on SO:
If I use auto x = new Object(); x is a pointer to address of Object and this is in dynamic memory and exists until I delete it.
However if I use Object x; or auto x = Object() it only lasts until it goes out of scope.
In an example they have shown this:
void foo()
{
Point p = Point(0,0);
} // p is now destroyed.
What I don't understand is what happens when I return a object when I don't use new? Will it be a copy of the object?
Here is an example of what I am not sure about:
class Object
{
public:
int X;
static Object Foo(int y)
{
Object result;
result.X = y;
return result;
}
};
class TestContainer
{
public:
void Run()
{
for(auto i = 0; i < 10; i++)
{
_objects.at(i) = Object::Foo(i + (rand() % 10 + 1));
}
}
private:
std::vector<Object> _objects;
};
void main()
{
TestContainer tc;
while(true)
{
tc.Run();
}
}
Note I haven't tested this code but I think it illiterates my confusion. In my main function I instantiate TestContainer and endless call it's Run method. This in turn loops calling a static Foo method on Object that returns a copy of a new Object, which is stored in a vector.
My question is, what happens with all the Object's? If I replace element 2 in the objects vector with a new Object, is the old value now "out of scope" and is deleted?
Will it be a copy of the object?
Yes.
Or a move could be used instead, or the entire thing could be optimised away to produce only one actual object in your final, compiled program.
But, basically, yes.
If I replace element 2 in the objects vector with a new Object, is the old value now "out of scope" and is deleted?
Yes.
As an aside, you're using at on elements that don't exist; to add elements, use insert or push_back.
A simple class like this behaves much like a POD variable. o1=o2 copies the fields, element-wise. So the target Object of an assignment does not get deleted but overwritten.
Objects which go out of scope "go away" (because the stack is unwound) like e.g. an int.
Here is a run-able example that I believe illustrates this behavior:
#include <iostream>
using namespace std;
class Foo {
private:
int id;
public:
Foo(int x)
: id(x)
{
cout << this->id << " is created" << endl;
}
Foo(const Foo& rhs)
: id(rhs.id)
{
cout << "copied " << this->id << endl;
}
Foo& operator=(Foo rhs){
this->id=rhs.id;
cout << "assigned " << this->id << endl;
return *this;
}
~Foo(){
cout << this->id << " is destroyed" << endl;
}
int getID(){
return this->id;
}
};
Foo bar(){
Foo f1 = Foo(1);
cout << f1.getID() << " from bar" << endl;
return f1;
}
int main(){
Foo f2 = bar();
cout << f2.getID() << " from main" << endl;
return 0;
}
This produces this output:
1 is created
1 from bar
1 from main
1 is destroyed
From this, I'm not seeing a copy or an assignment. I suspect what is happening is that both f1 and f2 are referencing the same instance of the object in memory. The object is not being de-allocated when the f1 reference goes out of scope because the object has another reference assigned to it.
As indicated in the title above, my question is simply whether or not a C++ cast does create a new object of the target class. Of course, I have used Google, MSDN, IBM and stackoverflow's search tool before asking this but I can't find an appropriate answer to my question.
Lets consider the following implementation of the diamond problem solved by using virtual inheritance:
#include <iostream>
#include <cstdlib>
struct A
{
int a;
A(): a(2) { }
};
struct B: virtual public A
{
int b;
B(): b(7) { }
};
struct C: virtual public A
{
int c;
C(): c(1) { }
};
struct END: virtual public B, virtual public C
{
int end;
END(): end(8) { }
};
int main()
{
END *end = new END();
A *a = dynamic_cast<A*>(end);
B *b = dynamic_cast<B*>(end);
C *c = dynamic_cast<C*>(end);
std::cout << "Values of a:\na->a: " << a->a << "\n\n";
std::cout << "Values of b:\nb->a: " << b->a << "\nb->b: " << b->b << "\n\n";
std::cout << "Values of c:\nc->a: " << c->a << "\nc->c: " << c->c << "\n\n";
std::cout << "Handle of end: " << end << "\n";
std::cout << "Handle of a: " << a << "\n";
std::cout << "Handle of b: " << b << "\n";
std::cout << "Handle of c: " << c << "\n\n";
system("PAUSE");
return 0;
}
As I understood, the actual structure of B and C, which normally consists of both an embedded instance of A and variables of B resp. C, is destroyed since the virtual A of B and C is merged to one embedded object in END to avoid ambiguities. Since (as I always thought) dynamic_cast usually only increases the address stored by a pointer by the offset of the embedded (cast's) target class there will be a problem due to the fact that the target (B or C) class is divided into several parts.
But if I run the example with MSVC++ 2011 Express everything will happen as expected (i.e. it will run, all *.a output 2), the pointers only slightly differ. Therefor, I suspect that the casts nevertheless only move the addresses of the source pointers by the internal offset of B's / C's instance.
But how? How does the resulting instance of B / C know the position of the shared A object. Since there is only one A object inside the END object but normally an A object in B and C, either B or C must not have an instance of A, but, indeed, both seem to have an instance of it.
Or does virtual only delegate calls to A's members to a central A object without deleting the respective A objects of each base class which inherits virtual from A (i.e. does virtual actually not destroy the internal structure of inherited and therefor embedded objects but only not using their virtualized (= shared) members)?
Or does virtual create a new "offset map" (i.e. the map which tells the address offsets of all members relative to the pointer to a class instance, I dunno the actual term) for such casted objects to handle their "distributedness"?
I hope I have clarified everything, many thanks in advance
BlueBlobb
PS:
I'm sorry if there are some grammar mistakes, I'm only a beer loving Bavarian, not a native speaker :P
Edit:
If have added these lines to output the addresses of all int a's:
std::cout << "Handle of end.a: " << &end->a << "\n";
std::cout << "Handle of a.a: " << &a->a << "\n";
std::cout << "Handle of a.b: " << &b->a << "\n";
std::cout << "Handle of a.c: " << &c->a << "\n\n";
They are the same implying that there is indeed only one A object.
my question is simply whether or not a C++ cast does create a new object of the target class.
Yes, a cast to a class type would create new temporary object of that type.
Note that your example doesn't cast to a class anywhere: the only casts it performs are to pointer types. Those casts do create new instances of pointers - but not of the objects pointed to. I'm not sure what your example was supposed to demonstrate, nor how it is related to your stated question.
Also, dynamic_cast is unnecessary where you use it; an implicit conversion would work just as well.
Since (as I always thought) dynamic_cast usually only increases the address stored by a pointer by the offset of the embedded (cast's) target class
You must be thinking of static_cast or something. dynamic_cast is much more powerful. For example, it can cast from B* to C*, even though they are unrelated at compile time, by going down to END* and then back up the other branch. dynamic_cast utilizes run-time type information.
How does the resulting instance of B / C know the position of the shared A object.
This is implementation-dependent. A typical implementation would reserve space within the derived class instance to store an offset to its virtual base class instance. The constructor of the most-derived class initializes all those offsets.
No, you're just seeing the effects of multiple inheritance. In order for a pointer to be cast to a different base type, it has to be adjusted to the part of the object that represents that exact type. The compiler knows the original type of the pointer and the result type, so it can apply the necessary offsets. In order for the derived type to satisfy the "is-a" requirement it must have the necessary structure built in to emulate all of the base types.
There's one case where a cast can create a new object, and that's when you're casting to a type other than a pointer or reference type. Often that won't be possible unless you've defined a cast operator for that type.
The example you gave uses pointers.
A* a = dynamic_cast<A*>(end);
So the only "new" thing created here is another pointer, which will point to the "A" vtable of the object to which "end" points. It does not actually construct a new object of the class/struct types you are using.
Contrast with
A a;
B b(a);
Here a new object is created. But otherwise, casting does not create a new object of the destination cast type.
The reason the pointers differ is because they are pointing to the different vtables that preceed the data section of the underlying object.
Example:
#include <iostream>
using namespace std;
struct A {
int a[64];
A() { cout << "A()" << endl; }
A(const A&) { cout << "A(A&)" << endl; }
A& operator = (const A&) { cout << "A=A" << endl; return *this; }
};
struct B : virtual public A {
int b[64];
B() { cout << "B()" << endl; }
B(const B&) { cout << "B(B&)" << endl; }
B(const A&) { cout << "B(A&)" << endl; }
B& operator = (const B&) { cout << "B=B" << endl; return *this; }
B& operator = (const A&) { cout << "B=A" << endl; return *this; }
};
struct C : virtual public A {
int c[64];
C() { cout << "C()" << endl; }
C(const C&) { cout << "C(C&)" << endl; }
C(const B&) { cout << "C(B&)" << endl; }
C(const A&) { cout << "C(A&)" << endl; }
C& operator = (const C&) { cout << "C=C" << endl; return *this; }
C& operator = (const B&) { cout << "C=B" << endl; return *this; }
C& operator = (const A&) { cout << "C=A" << endl; return *this; }
};
struct END : virtual public B, C {
int end[64];
END() { cout << "END()" << endl; }
END(const END&) { cout << "END(END&)" << endl; }
END(const C&) { cout << "END(C&)" << endl; }
END(const B&) { cout << "END(B&)" << endl; }
END(const A&) { cout << "END(A&)" << endl; }
END& operator = (const END&) { cout << "END=END" << endl; return *this; }
END& operator = (const C&) { cout << "END=C" << endl; return *this; }
END& operator = (const B&) { cout << "END=B" << endl; return *this; }
END& operator = (const A&) { cout << "END=A" << endl; return *this; }
};
int main() {
END* end = new END();
A *a = dynamic_cast<A*>(end);
B *b = dynamic_cast<B*>(end);
C *c = dynamic_cast<C*>(end);
std::cout << "end = " << (void*)end << std::endl;
std::cout << "a = " << (void*)a << std::endl;
std::cout << "b = " << (void*)b << std::endl;
std::cout << "c = " << (void*)c << std::endl;
// the direct pointers are going to have to differ
// to point to the correct vtable. what about 'a' in all cases?
std::cout << "end->a = " << (void*)&(end->a) << std::endl;
std::cout << "a->a = " << (void*)&(a->a) << std::endl;
std::cout << "b->a = " << (void*)&(b->a) << std::endl;
std::cout << "c->a = " << (void*)&(c->a) << std::endl;
}
Which you can see running here: http://ideone.com/0QAoWE
At least with MSVC in VS 2017, the answer is a definite maybe.
// Value is a struct that contains a member: std::string _string;
// _value is a std::variant<> containing a Value as one member
template <> std::string const &Get<std::string>() const
{
// Required pre-condition: _value.index() == TYPE_VALUE
Value const &value = std::get<TYPE_VALUE>(_value);
return static_cast<std::string>(value._string);
}
std::string const &test()
{
static std::string x = "hello world";
return static_cast<std::string>(x);
}
Get() is a very small snippet from a much larger project, and won't operate without the support of several hundred other lines of code. test() is something I quickly threw together to investigate.
As written, Get()generates the following warning:
warning C4172: returning address of local variable or temporary
while test() compiles clean. If I remove the static_cast<> from Get(), it also compiles cleanly.
P.S. in hindsight, I ought to rename _value to something like _payload, since it can contain a lot more than a Value.