codingdir logo sitemap sitemap |

C++11 class lattice with mixed virtual and non-virtual bases?

By : , Category : c++

Which of the following is the class lattice?



#include <iostream>

struct B {};

struct BV : virtual B {};
struct BN : B {};

struct C1 : BV, BN {};
struct C2 : BV, BN {};

struct D : C1, C2 {};

    D d;
    C1* c1 = static_cast<C1*>(&d);
    BV* bv1 = static_cast<BV*>(c1);
    BN* bn1 = static_cast<BN*>(c1);
    B* b1 = static_cast<B*>(bv1);
    B* b2 = static_cast<B*>(bn1);
    C2* c2 = static_cast<C2*>(&d);
    BV* bv2 = static_cast<BV*>(c2);
    BN* bn2 = static_cast<BN*>(c2);
    B* b3 = static_cast<B*>(bv2);
    B* b4 = static_cast<B*>(bn2);
    std::cout << "d = " << &d << '
    std::cout << "c1 = " << c1 << '
    std::cout << "c2 = " << c2 << '
    std::cout << "bv1 = " << bv1 << '
    std::cout << "bv2 = " << bv2 << '
    std::cout << "bn1 = " << bn1 << '
    std::cout << "bn2 = " << bn2 << '
    std::cout << "b1 = " << b1 << '
    std::cout << "b2 = " << b2 << '
    std::cout << "b3 = " << b3 << '
    std::cout << "b4 = " << b4 << '

My output:

d = 0x7fff5ca18998
c1 = 0x7fff5ca18998
c2 = 0x7fff5ca189a0
bv1 = 0x7fff5ca18998
bv2 = 0x7fff5ca189a0
bn1 = 0x7fff5ca18998
bn2 = 0x7fff5ca189a0
b1 = 0x7fff5ca189a8
b2 = 0x7fff5ca18998
b3 = 0x7fff5ca189a8
b4 = 0x7fff5ca189a0

If it is (2) wouldn't it be sufficient to say "for each occurence of a base class in the class lattice there is a corresponding base class subobject" ?. That is, if the construction of the lattice already depends on virtual and non-virtual base class relationships to select edges and vertices?

Merging your suggestion...

A base class specifier that contains the keyword virtual, specifies a virtual base class. For each distinct occurrence of a non-virtual base class in the class lattice of the most derived class, the most derived object (1.8) shall contain there is a corresponding distinct base class subobject of that type. For each distinct base class that is specified virtual, the most derived object shall contain a single base class subobject of that type.

I'm not an expert in the language half of the standard. However when I read your modified specification, I don't see how:

class V { /∗...∗/ };
class A : virtual public V { /∗ ... ∗/ };
class B : virtual public V { /∗ ... ∗/ };
class C : public A, public B { /∗...∗/ };

Results in Figure 4:

A     B

I don't see another place in the standard that specifies that although V appears twice in the class hierarchy below C, only one subobject of type V actually exists because of the use of the virtual keyword.

ReLated :

It's a bug. (55173, 55367, 55453) The bug is specific to gcc-i686-mingw versions 4.7.0 to 4.7.2.

From 55173:

When a virtual call invoked on an object which uses both virtual and non-virtual inheritance, the virtual thunk leaves the target function with an invalid "this" pointer.

It's basically what I've described in the question. The register that should hold this was used in non-related operations.

The only solution is to downgrade below 4.7.0 or upgrade to 4.7.3 or above.

There is no standard answer for your question. Its really depending on the compiler version. There is no standard ABI specified in C++. If you are interested deeper please take a look at "Itanium C++ ABI" or try to find the answer on your own by looking into the asembler code.

There was even a proposal to define portable ABI for C++

Since ~concept() is virtual, when you delete the object via a concept pointer, it will call ~implementation().

I don't see any reason why the rest will not work like they are supposed to.

Any class in a hierarchy can list its virtual base classes among its mem-initializer-list (the part of the constructor definition after :), but only one of them will actually execute these constructors: the most derived class.

Let's imagine a setup like this:

#include <iostream>

struct V
  explicit V(char c) { std::cout << c << '
'; }

struct A : virtual V
  A() : V('a') {}

struct B : A, virtual V
  B() : V('b') {}

struct C : B
  C() : V('c') {}

int main()
  A a; // prints 'a'
  B b; // prints 'b'
  C c; // prints 'c'

[Live example]

As you can see, even though all of A, B, and C initialise V in their constructor, only the most derived class (the type of the object you're actually creating) executes its initialiser.

Now let's imagine we add a class D like this:

struct D : C
  D() {}

This will not compileD does not list V among its mem-initializer-list, so the default constructor is to be used, but that doesn't exist.

To address your concrete example with derived derived from both derived1 and derived2: when creating an object of type derived, derived is the most derived type therefore only the constructor of derived itself can pass arguments to the base constructor. Any initialisation of base inside the constructors of derived1 and derived2 will simply be ignored when creating a derived object.


Message :
Login to Add Your Comments .
How to disable registered OpenCL platforms on Windows?
Is Observable broken in Angular 2 Beta 3?
Cross-thread operation not valid when using Invoke
How to pass an IEnumerable or queryable list of properties from Controller to View
Finding numbers after a certain keyword using Python
Pocketsphinx recognizes random phrases in a silence
Passing non-thread-safe objects through thread-safe containers
React scroll nav
BizTalk WCF-BasicHttp Adapter does not allow Empty string for Service Certificate Props
Why property ''cause" of Exception is repeating forever?
Privacy Policy 2017 © All Rights Reserved .