Pergunta

When I try to compile the following code:

class A {
public:
    A(int v) : virt(v) { }
    int virt;
    int getVirt(void) const { return virt; }
};

class B : private virtual A {
protected:
    B(int v) : A(v) { }
    using A::getVirt;
};

class C : public B, private virtual A {
protected:
    C(int v) : A(v), B(v) { }
    using A::getVirt;
};

class D : public C {
public:
    D(void) : C(3) { }
    using C::getVirt;
};

#include <iostream>

int main(int argc, char *argv[]) {
    D d;
    std::cout << "The number is: " << d.getVirt() << std::endl;

    return 0;
}

I get an error about D not instantiating A; is that correct? If a virtual base is embedded in the hierarchy do all derived classes also need to derive from that base, virtually, so they can call the parametric constructor of the virtual base?

BTW, here are the errors produced by G++:

Main.cpp: In constructor ‘D::D()’:
Main.cpp:22:18: error: no matching function for call to ‘A::A()’
Main.cpp:22:18: note: candidates are:
Main.cpp:3:5: note: A::A(int)
Main.cpp:3:5: note:   candidate expects 1 argument, 0 provided
Main.cpp:1:7: note: A::A(const A&)
Main.cpp:1:7: note:   candidate expects 1 argument, 0 provided
Foi útil?

Solução 2

As Kerrek SB mentions, you need to initialize A in the constructor for D.

However, you must also explicitly tell the compiler that you are not accessing A from its (privately) derived context by using the scope operator.

class D : public C {
public:
    D(void) : ::A(3), C(3) { }
//            ^^ Access this constructor from a global context
    using C::getVirt;
};

This also means that your constructor must be public, as is already the case with your code.

Outras dicas

That has nothing to do with access control (at least not primarily). Rather, you have to understand how virtual bases work: The virtual base subobject is initialized by the most derived class. Since you don't mention A in the constructor initializer list of D, the default constructor is tried, but doesn't exist.

To fix this, initalize A properly in D:

 D() : A(3), C(3) { }

When you say A(3), name lookup is performed according to 12.6.2/2:

In a mem-initializer-id an initial unqualified identifier is looked up in the scope of the constructor’s class and, if not found in that scope, it is looked up in the scope containing the constructor’s definition.

As Drew Dorman rightly points out, you can force a direct path to the virtual base class by calling it ::A and thus obtaining the desired access.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top