C ++: A node containing class instances calls an invalid virtual function

I encountered a strange phenomenon when running the following code:

#include <iostream> class Piece { public: class Queen; class Knight; union Any; virtual const char* name() const = 0; }; class Piece::Queen : public Piece { public: virtual const char* name() const { return "Queen"; } }; class Piece::Knight : public Piece { public: virtual const char* name() const { return "Knight"; } }; union Piece::Any { public: Any() {} Piece::Queen queen; Piece::Knight knight; }; using namespace std; int main(int argc, const char* argv[]) { Piece::Any any; any.queen = Piece::Queen(); cout << any.queen.name() << endl; return 0; } 

The program was compiled in the Apple LLVM 3.0 compiler, but the output was Knight. I expected the output to be "Queen." From my testing, I saw that when Piece :: Any constructor works by default, it calls the Piece :: Queen and Piece :: Knights constructs, one after the other. If I were to declare Piece :: Any like this:

 union Piece::Any { public: Any() {} Piece::Knight knight; Piece::Queen queen; }; 

(I basically changed the order of the knight and queen), then the exit will be the queen. Any help would be appreciated.

thanks

+4
source share
2 answers

First of all - your constructor does not seem to initialize any of its members. You must choose one, for example

 Piece::Any::Any(): knight() {} 

Then according to 9.5.4

In general, you need to use explicit calls to the destructor and assign new operators to change the active member of the union

therefore the correct switch from a knight to a queen

 any.knight.~Knight(); new(&any.queen) Queen; 

If this looks ugly for you (as it does for me), this is a clear indication that storing objects with non-trivial constructors in a union is not a good idea (what about boost :: variant?).

+4
source
 any.queen = Piece::Queen(); 

This does not mean what you think. It is equivalent

 any.queen.operator=(Piece::Queen()); 

which cannot work reliably if any.queen does not exist (since you did not force your union contain the active element).

You really need to initialize the member you want to use, for example:

 new (&any.queen) Piece::Queen; 
+3
source

Source: https://habr.com/ru/post/1399548/


All Articles