Is it legal to have statically-allocated pure-virtual-parent-class references to statically-allocated child class objects?

1

UPDATE: I am not asking people to try this out & see if the code works for them. I am asking whether the code pattern is legal C++, regardless of whether it works for you.

I am investigating what I believe is a bug in the IAR C++ compiler for Renesas RX CPUs. Examples of the following code pattern sometimes work on my small embedded system, other times it crashes during initialization of parentRefToChildInstance in a jump to address 0x00000000 (or nearby; I've seen a jump to 0x00000038 as well). For a given version of the source code the behavior seems to be consistent between compilations, but if the code is perturbed in seemingly irrelevant ways, sometimes the behavior switches.

Is it legal to have pure-virtual-parent-class references to statically-allocated child class objects, or is it not legal because the order of initialization of statically allocated objects cannot be guaranteed?

char aGlobalVar = 0;

struct parent
{
   virtual ~parent() {}
   virtual void method1() = 0;
};

struct child : public parent
{
   child(int someValue) : m_someData(someValue) {}
   virtual ~child() {}
   virtual void method1() { ++aGlobalVar; }
   int m_someData;
};

child childInstance(0x1234abcd);
parent &parentRefToChildInstance = childInstance;

In cases where the crash occurs, the child class object has not been constructed at the point that the parent-class reference is initialized; I'm suspecting the compiler is somehow using the child object's vtable pointer to initialize the parent-class reference, though I haven't confirmed that for certain. But I thought the compiler should be able to initialize a reference knowing only the type of object it's referencing, and its address, both of which should be known at compile-time and link-time, respectively. If that's true, then it seems like it should not matter which order childInstance and parentRefToChildInstance are initialized.

Also, we're still limited to C++03, if that matters.

Here's a main() to go along with the above code...

int main()
{
   printf("aGlobalVar = %u\n", aGlobalVar);
   childInstance.method1();
   printf("aGlobalVar = %u\n", aGlobalVar);
   parentRefToChildInstance.method1();
   printf("aGlobalVar = %u\n", aGlobalVar);
}

Normally I would expect it to print this, not crash during static object initialization (even before main() starts):

aGlobalVar = 0
aGlobalVar = 1
aGlobalVar = 2
c++
c++03
asked on Stack Overflow Aug 23, 2018 by phonetagger • edited Aug 24, 2018 by phonetagger

1 Answer

2

The code shown is legal.

It's true that the order of initialization of objects and references defined in namespace scope or as static class members is unpredictable when the definitions are in different translation units, and this can often lead to nasty problems.

But initializing a reference doesn't actually require the bound object to be initialized, unless virtual inheritance gets involved.

C++17 [basic.life] paragraph 7 says:

Before the lifetime of an object has started but after the storage which the object will occupy has been allocated..., any glvalue that refers to the original object may be used but only in limited ways. For an object under construction or destruction, see [class.cdtor]. Otherwise, such a glvalue refers to allocated storage, and using the properties of the glvalue that do not depend on its value is well-defined. The program has undefined behavior if:

  • the glvalue is used to access the object, or

  • the glvalue is used to call a non-static member function of the object, or

  • the glvalue is bound to a reference to a virtual base class, or

  • the glvalue is used as the operand of a dynamic_cast or as the operand of typeid.

None of those four things are happening during the initialization of parentRefToChildInstance, in particular because parent is not a virtual base class of child. So the code falls into the case mentioned in the quoted requirement as being well-defined.

answered on Stack Overflow Aug 24, 2018 by aschepler • edited Jun 20, 2020 by Community

User contributions licensed under CC BY-SA 3.0