Search code examples
c++pointersinitializationvirtualunions

C++ initializer lists with unions, why different results?


I have the following program:

#include <initializer_list>
#include <iostream>

class A;
class nih {
public:
    virtual void apply(A & a) const { std::cout << "generic" << std::endl; };
};

union niit;
class A {
public:
  int s; char kau;
  A() : s(1), kau('-') { }
  A(std::initializer_list<niit> k);
};

class sn : public nih { int s; public: sn(int x) : s(x) { }
  virtual void apply(A & a) const { a.s=s; } };
class kaun : public nih { char kau; public: kaun(char x) : kau(x) { }
  virtual void apply(A & a) const { a.kau=kau; } };
union niit { sn sni; kaun kauni; nih nihi;
  niit(const sn & s) : sni(s) { }
  niit(const kaun & k) : kauni(k) { }
};

A::A(std::initializer_list<niit> k) { 
  const niit *n=k.begin();
  for(int i=0;i<k.size();i++) {
    const nih* p=&n[i].nihi;
    p->apply(*this);
  }
}

int main(int argc, char**argv) {
  A r {kaun('a'),sn(91)};
  std::cout << r.s << r.kau << std::endl;
}

I get the expected result, which is 91a. However, if I change the line

p->apply(*this);

to the following:

((const nih*)(&n[i].nihi))->apply(*this);

which should be replacement with an equivalent value, it no longer works, and instead it prints:

generic
generic
0

That is, it executes the generic method from nih. Why is that? I tried gcc 4.6 with option -std=c++0x, in case it matters.


Solution

  • A union can only be used with POD types, and as such cannot have members with virtual functions or nontrivial constructors/destructors, so this is undefined behaviour. Read (taken from this answer) §9.5.1 of the standard:

    A union can have member functions (including constructors and destructors), but not virtual functions. A union shall not have base classes. A union shall not be used as a base class. An object of a class with a non-trivial constructor, a non-trivial copy-constructor, a non-trivial destructor, or a non-trivial copy assignment operator cannot be a member of a union, nor can an array of such objects. If a union contains a static data member, or a member of a reference type, the program is ill-formed.

    And

    In certain contexts, C++ allows only POD types to be used. For example, a union in C++ cannot contain a class that has virtual functions, or nontrivial constructors or destructors. This restriction is imposed because the compiler cannot know which constructor or destructor should be called for a union.