why c++ auto object optimization is happening in this code? - c++

Why only one object of A is created in this program? and no copy constructor is called. What is this optimization called? If this is a valid/known optimization, will it not be a trouble for multiton design pattern?
#include <iostream>
#include <stdio.h>
using namespace std;
class A
{
public:
A () {
cout << "in-- Constructor A" << endl;
++as;
}
A (const A &a) {
cout << "in-- Copy-Constructor A" << endl;
++as;
}
~A() {
cout << "out --Constructor A" << endl;
--as;
}
A & operator=(const A &a) {
cout << "assignment" << endl;
++as;
//return *this;
}
static int as;
};
int A::as = 0;
A fxn() {
A a;
cout << "a: " << &a << endl;
return a;
}
int main() {
A b = fxn();
cout << "b: " << &b << endl;
cout << "did destructor of object a in func fxn called?" << endl;
return 0;
}
Output of above program
in-- Constructor A
a: 0x7fffeca3bed7
b: 0x7fffeca3bed7
did destructor of object a in func fxn called?
out --Constructor A

Go through link http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/
It will help you.

This seems to be a case of a return value optimization. This optimization is notable in the C++ world as it is allowed to change the observable behavior of the program. It's one of the few, if not the only, optimization that's allowed to do that, and it's from the days where returning copies was considered a weakness. (With move semantics and generally faster machines, this is much, much less of an issue now.)
Basically, the compiler sees that it's going to copy the object, so instead it allocates room for it on the calling frame and then builds it there instead of calling the copy constructor.

Related

Does destructing objects stop the late binding?

Consider the following C++ classes inheritance hierarchy and their intended polymorphic behaviour.
#include <iostream>
using namespace std;
class A
{
public:
A () { cout << "A constructor\n"; }
virtual void display() { cout << "A display\n"; }
virtual ~A() { cout << "A destructor\n"; }
friend ostream& operator << (ostream &out, A &a) {
a.display();
return out;
}
};
class B : public A
{
public:
B () { cout << "B constructor\n"; }
virtual void display() { cout << "B display\n"; }
virtual ~B() { cout << "B destructor\n"; }
};
class C : public B
{
public:
C () { cout << "C constructor\n"; }
virtual void display() {cout << "C display\n";}
virtual ~C() { cout << "C destructor\n"; }
};
int main()
{
C c1;
cout << endl; c1.display(); cout << endl;
c1.~C();
cout << endl; c1.display(); cout << endl;
c1.~C();
cout << "=================================================" << endl;
C c2;
cout << endl << c2 << endl;
c2.~C();
cout << endl << c2 << endl;
c2.~C();
return 0;
}
My understanding is that the display member function is virtual and therefore will always behave as such. In the top part of the main program it behaves well; that is when we call c1.display() it prints "C display" message. This is true even after destructing the c1 object.
In the lower part, we don't call the display function. Instead we call the output stream friend function which will in turn call the display member function. In this case, it works perfectly well at first; that is cout << endl << c2 << endl; prints C display. This is expected because we are passing the c1 object to the ostream friend function by reference and therefore late binding will take care of which display member function to execute.
But then when we do the same cout << endl << c2 << endl; after destructing c2 object, strangely the display member function is no more behaving as expected. It calls the base class display and prints "A display" message.
I don't understand it? Does destructing objects stop the late binding?
Destroying an object makes it illegal to continue calling member functions of that object. Your code's behaviour is undefined, and whatever behaviour you are observing cannot be relied upon to persist across even runs of the same program, let alone different compiler flags or entirely different implementations.
However, we can guess why you are seeing the particular behaviour you report, with the understanding that we should not continue to compile and run such code in the future as its behaviour is not guaranteed.
It is likely that your compiler is devirtualizing the c1.display() call since it can see the definition of c1 (and thus knows that its dynamic type is C). Thus, the generated code likely does not consult the vtable at all. This explains why you continue to see "C display" even though the object has already been destroyed.
In the c2 case, the object is being passed by reference and the compiler is probably not inlining aggressively enough to devirtualize the eventual display call. If it consults the vtable after c2 is destroyed, it probably finds the vtable for A, because the destruction process of the C object must eventually reset the A subobject's vptr to point to the A vtable prior to the execution of A::~A (in case A::~A calls any virtual functions). So that explains the "A display" message.

Strange object assignment behaviour c++

I have a strange behavior with object assignments. I will much appreciate, if you can explain why this assignment works like this. It has cost me already a lot of time.
I am using Visual Studio Enterprise 2017 (all default settings).
Code:
#include "stdafx.h"
#include <iostream>
using namespace std;
class Test
{
public:
Test()
{
cout << "Constructor of " << this << endl;
}
~Test()
{
cout << "Destructor of " << this << endl;
}
};
int main()
{
cout << "Assignment 1" << endl;
auto t = Test();
cout << "Assignment 2" << endl;
t = Test();
int i = 0;
cin >> i;
return 0;
}
Output (up to cin):
Assignment 1
Constructor of 006FFC9F
Assignment 2
Constructor of 006FFBC7
Destructor of 006FFBC7
Expected Output (up to cin):
Assignment 1
Constructor of 006FFC9F
Assignment 2
Destructor of 006FFC9F
Constructor of 006FFBC7
I wanted to write a test function which creates an object of my (template) class, do some tests, then create a new object and do some more testing. The problem is that t holds the already destructed object after the second assignment.
I know that I can just use dynamic allocation which results in the expected behavior, but why does this program behave different?
Thank you very much.
Regards.
PS: Results are the same, independent of Release/Debug or 64/32 bit compilation
EDIT: More verbose example:
#include "stdafx.h"
#include <iostream>
using namespace std;
class Test
{
private:
float* val;
public:
Test()
{
val = new float;
cout << "Constructor of " << this << ", addr. of val: " << val << endl;
}
~Test()
{
cout << "Destructor of " << this << ", addr. of val: " << val << " --> DELETING VAL!" << endl;
delete val;
}
float* getVal() { return this->val; }
};
int main()
{
cout << "Assignment 1" << endl;
auto t = Test();
cout << "Assignment 2" << endl;
t = Test();
cout << "Val Address: " << t.getVal() << endl;
int i = 0;
cin >> i;
return 0;
}
Output (it holds a deleted pointer at the end!!!):
Assignment 1
Constructor of 004FFBDC, addr. of val: 0072AEB0
Assignment 2
Constructor of 004FFB04, addr. of val: 00723928
Destructor of 004FFB04, addr. of val: 00723928 --> DELETING VAL!
Val Address: 00723928
With
auto t = Test();
you actually construct two objects. First is the Test() which constructs a temporary object. The second is the construction of t which is made through copy-construction. There is no assignment being made here, even if the = operator is used, it's copy-construction.
If you add a copy-constructor to the Test class similar to your constructor and destructor, you should see it clearly.
As for
t = Test();
here a temporary object is created with Test(). That temporary object is then passed to the (compiler-generated) assignment operator of the Test class, and then the temporary object is promptly destructed.
The object t itself is not destructed, it should not be as it is the destination of the assignment.
Your confusion seems to be a mistaken expectation that the original object is destroyed when assignment takes place. Like, in this code:
cout << "Assignment 2" << endl;
t = Test();
This piece of code invokes the move-assign operator. Since you didn't define one, the default one generated by the compiler is more-or-less going to look like this:
Test & operator=(Test &&) {}
Note how there's no invocation of a constructor or (critically) a destructor in that code. The only Constructors and Destructors that are going to run are on the temporary object (which is what you observe in the actual output). The original object doesn't get destroyed until the code goes out of scope; and why would it? It's not like you can stop using the stack space before then.
Edit: Something which might help you understand what's going on:
#include<iostream>
struct Test {
Test() {std::cout << "Constructed.\n";}
~Test() {std::cout << "Destructed.\n";}
Test(Test const&) {std::cout << "Copy-Constructed.\n";}
Test(Test &&) {std::cout << "Move-Constructed.\n";}
Test & operator=(Test const&) {std::cout << "Copy-Assigned.\n"; return *this;}
Test & operator=(Test &&) {std::cout << "Move-Assigned.\n"; return *this;}
};
int main() {
std::cout << "Test t;\n";
Test t; //Construction
std::cout << "Test t2(t);\n";
Test t2(t); //Copy-Construct
std::cout << "Test t3(std::move(t2));\n";
Test t3(std::move(t2)); //Move-Construct
std::cout << "Test t4 = t;\n";
Test t4 = t; //Copy Construct, due to Copy Ellision
std::cout << "Test t5 = Test();\n";
Test t5 = Test(); //Will probably be a normal Construct, due to Copy Ellision
std::cout << "t = t2;\n";
t = t2; //Copy Assign
std::cout << "t = Test();\n";
t = Test(); //Move Assign, will invoke Constructor and Destructor on temporary
std::cout << "Done! Cleanup will now happen!\n";
return 0;
}
Results as seen when compiled here:
Test t;
Constructed.
Test t2(t);
Copy-Constructed.
Test t3(std::move(t2));
Move-Constructed.
Test t4 = t;
Copy-Constructed.
Test t5 = Test();
Constructed.
t = t2;
Copy-Assigned.
t = Test();
Constructed.
Move-Assigned.
Destructed.
Done! Cleanup will now happen!
Destructed.
Destructed.
Destructed.
Destructed.
Destructed.
DOUBLE EDIT COMBO!:
As I mentioned in comments, val is just a pointer. 8 bytes (on a 64-bit machine) allocated as part of Test's storage. If you're trying to make sure that Test always contains a valid value for val that hasn't been deleted, you need to implement the Rule of Five (previously known as the Rule of Three):
class Test {
float * val;
public:
Test() {val = new float;}
~Test() {delete val;
Test(Test const& t) {
val = new float(*(t.val));
}
Test(Test && t) {std::swap(val, t.val);}
Test & operator=(Test const& t) {
float * temp = new float(*(t.val)); //Gives Strong Exception Guarantee
delete val;
val = temp;
return *this;
}
Test & operator=(Test && t) {std::swap(val, t.val); return *this;};
float & get_val() const {return *val;} //Return by reference, not by pointer, to
//prevent accidental deletion.
};

Assignment at Initialization without Default Constructor

In this program, I completely understand why the first part of the main function fails and needs to be commented - there's no implicit default ctor after I've implemented the value ctor within TestingClass. Perfectly logical. However, I was a bit surprised to find that the second part (creation of test2 object) succeeds just fine, at least with gcc 4.8.4.
#include <iostream>
using namespace std;
class TestingClass
{
public:
TestingClass(int inVal)
{
val = inVal;
}
int val;
};
TestingClass testingCreator()
{
return TestingClass(100);
}
int main()
{
/*
TestingClass test1;
test1 = testingCreator();
cout << "Test1: " << test1.val << endl;
*/
TestingClass test2 = testingCreator();
cout << "Test2: " << test2.val << endl;
}
Thinking about it, it also makes sense, because the object, test2, will never have existed without having been constructed / initialized, but most people think of initialization in this way as just being a declaration and an assignment on one line. Clearly, though, initialization is more special than that, since this code works.
Is this standard C++? Is it guaranteed to work across compilers? I'm interested in how initialization in this way is different than just declare (using a default ctor) and then assign (via a temporary object created in the global function).
UPDATE: Added a copy ctor and a third case that clearly uses the copy ctor.
#include <iostream>
using namespace std;
class TestingClass
{
public:
TestingClass(const TestingClass &rhs)
{
cout << "In copy ctor" << endl;
this->val = rhs.val + 100;
}
TestingClass(int inVal)
{
val = inVal;
}
int val;
};
TestingClass testingCreator()
{
return TestingClass(100);
}
int main()
{
/*
TestingClass test1;
test1 = testingCreator();
cout << "Test1: " << test1.val << endl;
*/
TestingClass test2 = testingCreator();
cout << "Test2: " << test2.val << endl;
TestingClass test3(test2);
cout << "Test3: " << test3.val << endl;
}
This outputs:
Test2: 100
In copy ctor
Test3: 200
Your thinking on what TestingClass test2 = testingCreator(); does is flawed. When you see
type name = stuff;
You do not create name and then assign to it stuff. What you do is copy initialize name from stuff. This means you call the copy or move constructor. Generally this call can be elided by optimizing compilers but if it was not then that is what you would see. In either case the default constructor is never called.
In your first example
TestingClass test1;
Forces the default constructor to be called and since you do not have one you get an error.
test2 is defined by the copy constructor of TestingClass, taking the result of testingCreator as argument. The copy constructor TestingClass::TestingClass(const TestingClass&) is automatically generated by the compiler and the C++ standard guarantees that it copies the val field.

Delete array of classes without calling destructors

Consider we create the array using this way:
T* arr = new T[num];
And now because of some reasons we understood that we need simply delete that array but without calling any T destructors.
We all know that if we write next:
delete arr;
the T destructor will be called.
If we write this:
delete[] arr;
the num destructors would be called.
Having played with pointers, you realize that new inserts before the result pointer the unsigned long long value that represents the number of allocated T instances. So we try to outwit the C++ trying to change that value to number of bytes that arr occupies and delete it as (char*) in hope that in this case the delete would not call the destructors for T instances and simply free occupied memory. So you write something like this:
typedef unsigned long long;
unsll & num = *(unsll)((char*)arr-sizeof(unsll));
num = num*sizeof(T);
delete ((char*)arr);
But that doesn't work and C++ creates the trigger breakpoint(run time error) when trying to delete this. So that doesn't work. And a lot of other playing with pointers doesn't work as at least some error(compile- or run-time) occurs. So the question is:
Is that possible to delete an array of classes in C++ without calling their destructors?
Perhaps you want ::operator delete[](arr).
(See http://en.cppreference.com/w/cpp/memory/new/operator_delete)
But this still has undefined behaviour, and is a terrible idea.
One simple way to deallocate without calling destructors is to separate allocation and initialization. When you take proper care of alignment you can use placement new (or the functionality of a standard allocator object) to create the object instances inside the allocated block. Then at the end you can just deallocate the block, using the appropriate deallocation function.
I can't think of any situation where this would be a smart thing to do: it smells strongly of premature optimization and X/Y-problem (dealing with problem X by imagining impractical Y as a solution, then asking only about Y).
A new-expression is designed to couple allocation with initialization, so that they're executed as an all-or-nothing operation. This coupling, and ditto coupling for cleanup and deallocation, is key to correctness, and it also simplifies things a lot (i.e., inside there's complexity that one doesn't have to deal with). Uncoupling needs to have a very good reason. Avoiding destructor calls, for e.g. purposes of optimization, is not a good reason.
I'm only going to address your specific question of
Is that possible to delete an array of classes in C++ without calling their destructors?
The short answer is yes.
The long answer is yes, but there's caveats and considering specifically what a destructor is for (i.e. resource clean up), it's generally a bad idea to avoid calling a class destructor.
Before I continue the answer, it should be noted that this is specifically to answer your question and if you're using C++ (vs. straight C), using this code will work (since it's compliant), but if you're needing to produce code in this way, you might need to rethink some of your design since code like this can lead to bugs/errors and general undefined behavior if not used properly.
TL;DR if you need to avoid destructors, you need to rethink your design (i.e. use copy/move semantics or an STL container instead).
You can use malloc and free to avoid constructor and destructor calls, example code:
#include <iostream>
#include <cstdio>
class MyClass {
public:
MyClass() : m_val(0)
{
this->init(42);
std::cout << "ctor" << std::endl;
}
~MyClass()
{
std::cout << "dtor" << std::endl;
}
friend std::ostream& operator<<(std::ostream& stream, const MyClass& val)
{
stream << val.m_val;
return stream;
}
void init(int val)
{
/* just showing that the this pointer is valid and can
reference private members regardless of new or malloc */
this->_init(val);
}
private:
int m_val;
void _init(int val)
{
this->m_val = val;
}
};
template < typename Iterator >
void print(Iterator begin, Iterator end)
{
while (begin != end) {
std::cout << *begin << std::endl;
++begin;
}
}
void set(MyClass* arr, std::size_t count)
{
for (; count > 0; --count) {
arr[count-1].init(count);
}
}
int main(int argc, char* argv[])
{
std::cout << "Calling new[10], 10 ctors called" << std::endl;
MyClass* arr = new MyClass[10]; // 10 ctors called;
std::cout << "0: " << *arr << std::endl;
set(arr, 10);
print(arr, arr+10);
std::cout << "0: " << *arr << std::endl;
std::cout << "Calling delete[], 10 dtors called" << std::endl;
delete[] arr; // 10 dtors called;
std::cout << "Calling malloc(sizeof*10), 0 ctors called" << std::endl;
arr = static_cast<MyClass*>(std::malloc(sizeof(MyClass)*10)); // no ctors
std::cout << "0: " << *arr << std::endl;
set(arr, 10);
print(arr, arr+10);
std::cout << "0: " << *arr << std::endl;
std::cout << "Calling free(), 0 dtors called" << std::endl;
free(arr); // no dtors
return 0;
}
It should be noted that mixing new with free and/or malloc with delete results in undefined behavoir, so calling MyClass* arr = new MyClass[10]; and then call free(arr); might not work as "expected" (hence the UB).
Another issue that will arise from not calling a constructor/destructor in C++ is with inheritance. The above code will work with malloc and free for basic classes, but if you start to throw in more complex types, or inherit from other classes, the constructors/destructors of the inherited classes will not get called and things get ugly real quick, example:
#include <iostream>
#include <cstdio>
class Base {
public:
Base() : m_val(42)
{
std::cout << "Base()" << std::endl;
}
virtual ~Base()
{
std::cout << "~Base" << std::endl;
}
friend std::ostream& operator<<(std::ostream& stream, const Base& val)
{
stream << val.m_val;
return stream;
}
protected:
Base(int val) : m_val(val)
{
std::cout << "Base(" << val << ")" << std::endl;
}
void _init(int val)
{
this->m_val = val;
}
int m_val;
};
class Child : public virtual Base {
public:
Child() : Base(42)
{
std::cout << "Child()" << std::endl;
}
~Child()
{
std::cout << "~Child" << std::endl;
}
void init(int val)
{
this->_init(val);
}
};
template < typename Iterator >
void print(Iterator begin, Iterator end)
{
while (begin != end) {
std::cout << *begin << std::endl;
++begin;
}
}
void set(Child* arr, std::size_t count)
{
for (; count > 0; --count) {
arr[count-1].init(count);
}
}
int main(int argc, char* argv[])
{
std::cout << "Calling new[10], 20 ctors called" << std::endl;
Child* arr = new Child[10]; // 20 ctors called;
// will print the first element because of Base::operator<<
std::cout << "0: " << *arr << std::endl;
set(arr, 10);
print(arr, arr+10);
std::cout << "0: " << *arr << std::endl;
std::cout << "Calling delete[], 20 dtors called" << std::endl;
delete[] arr; // 20 dtors called;
std::cout << "Calling malloc(sizeof*10), 0 ctors called" << std::endl;
arr = static_cast<Child*>(std::malloc(sizeof(Child)*10)); // no ctors
std::cout << "The next line will seg-fault" << std::endl;
// Segfault because the base pointers were never initialized
std::cout << "0: " << *arr << std::endl; // segfault
set(arr, 10);
print(arr, arr+10);
std::cout << "0: " << *arr << std::endl;
std::cout << "Calling free(), 0 dtors called" << std::endl;
free(arr); // no dtors
return 0;
}
The above code is compliant and compiles without error on g++ and Visual Studio, but due to the inheritance, both crash when I try to print the first element after a malloc (because the base class was never initialized).
So you can indeed create and delete an array of objects without calling their constructors and destructors, but doing so results in a slew of extra scenarios you need to be aware of and account for to avoid undefined behavior or crashes, and if this is the case for your code, such that you need to ensure the destructors are not called, you might want to reconsider your overall design (possibly even use an STL container or smart pointer types).
Hope that can help.

C++ Does casting create a new object?

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.