virtual inheritance nightmare

G

Gianni Mariani

In the code below, controller::controller() is never invoked, however,
it appears there is no way to make a compile-time rule that this should
not happen. The code below seems to make compilers complain that
controller::controller() is private, even though it is never used.

What do others do to work-around this ? I suppose I can simply
not implement controller::controller(), that way I get a linking
error if there exists errant code. (ok tried that - gcc seems to
detect errors and all is fine, MSVC seems to need the constructor
even though it should never be invoked.)

Especially when a class has no way of being constructed alone (due to
pure virtual methods), there should be not reason to access default
virtual constructors since they can never be called hence there should
be no violation of the access rule.

class controller
{
controller(); // private don't want anyone to call this
public:
controller( int ); // this should be called instead
};

class Interface
: virtual public controller
{
public:
virtual void DoThing() = 0;
};

class Interface_Impl1
: public Interface
{
public:

virtual void DoThing1() = 0;

virtual void DoThing()
{
DoThing1();
}
};

class Interface_Impl2
: public Interface
{
public:

virtual void DoThing2() = 0;

virtual void DoThing()
{
DoThing2();
}
};

class Interface_Impl3
: public Interface
{
public:

virtual void DoThing()
{
// doing 3
}
};

class Application
: public Interface_Impl1,
public Interface_Impl2
{
Application()
: controller( 3 )
{
}

void DoThing1()
{
// Doing 1 it here !
}

void DoThing2()
{
// Doing 2 it here !
}
};
 
V

Victor Bazarov

Gianni said:
In the code below, controller::controller() is never invoked, however,
it appears there is no way to make a compile-time rule that this should
not happen. The code below seems to make compilers complain that
controller::controller() is private, even though it is never used.

But it _might_ be used if you derive from Interface and make that class
concrete.
What do others do to work-around this ? I suppose I can simply
not implement controller::controller(), that way I get a linking
error if there exists errant code. (ok tried that - gcc seems to
detect errors and all is fine, MSVC seems to need the constructor
even though it should never be invoked.)

But Impl1 and Impl2 also don't have default c-tors... And those _are_
invoked, no?
Especially when a class has no way of being constructed alone (due to
pure virtual methods), there should be not reason to access default
virtual constructors since they can never be called hence there should
be no violation of the access rule.

That's a QOI issue, I believe.
class controller
{
controller(); // private don't want anyone to call this
public:
controller( int ); // this should be called instead
};

class Interface
: virtual public controller
{
public:
virtual void DoThing() = 0;
};

As is, 'Interface' cannot have a default c-tor because its base class,
'controller' cannot be instantiated. Right?
class Interface_Impl1
: public Interface
{
public:

virtual void DoThing1() = 0;

virtual void DoThing()
{
DoThing1();
}
};

Now, 'Interface_Impl1' cannot have the default c-tor since 'Interface',
which is its base class, cannot have one. Right?
class Interface_Impl2
: public Interface
{
public:

virtual void DoThing2() = 0;

virtual void DoThing()
{
DoThing2();
}
};

Now, 'Interface_Impl2' cannot have the default c-tor as well, due to the
same reason as the 'Interface_Impl1'. Right?
class Interface_Impl3
: public Interface
{
public:

virtual void DoThing()
{
// doing 3
}
};

class Application
: public Interface_Impl1,
public Interface_Impl2
{
Application()
: controller( 3 )

Wait... Here 'Interface_Impl1' and 'Interface_Impl2' _are_ instantiated
using their default c-tor, which cannot be generated because... See above.
{
}

void DoThing1()
{
// Doing 1 it here !
}

void DoThing2()
{
// Doing 2 it here !
}
};

The work-around here (as I see it) is to declare 'Interface*' constructors
to accept a single argument and give it a default value. Since the final
class 'Application' will provide the argument for 'controller', the other
argument (although you will thread it through to the 'controller's c-tor)
will not have any effect. Of course, you will have to initialise the
virtual base 'controller' in each of 'Interface*' constructors too, but
that, again, should have no effect on your code, since 'Application' takes
over.

BTW, your 'Application's constructor is also private.

V
 
G

Gianni Mariani

Victor said:
But it _might_ be used if you derive from Interface and make that class
concrete.

How could it possibly be used ? Interface is an abstract class which by
definition means it can't be instantiated alone. If it is made complete
by inheritance, then it is the derived class that will need to call the
controller() constructor. Am I wrong ?
But Impl1 and Impl2 also don't have default c-tors... And those _are_
invoked, no?

Same issue applies here, the controller() constructor can't be called in
Impl1 and Impl2 because they are never instantiated alone.
That's a QOI issue, I believe.

"Question of Intelligence" ?

....
Wait... Here 'Interface_Impl1' and 'Interface_Impl2' _are_ instantiated
using their default c-tor, which cannot be generated because... See above.

yeah, but controller() constructor must not be called.
The work-around here (as I see it) is to declare 'Interface*' constructors
to accept a single argument and give it a default value. Since the final
class 'Application' will provide the argument for 'controller', the other
argument (although you will thread it through to the 'controller's c-tor)
will not have any effect. Of course, you will have to initialise the
virtual base 'controller' in each of 'Interface*' constructors too, but
that, again, should have no effect on your code, since 'Application' takes
over.

That makes it far too onerous. Having a to write a whole bunch of dead
code that never gets executed just to satisfy the compiler seems a
nonsensical to me.
BTW, your 'Application's constructor is also private. yeah - just example code.

V

Thanks for the ideas !

I think I'll go with implementing a controller() constructor and putting
a big fat abort() in it. (perhaps on gcc, not implementing it at all !).

G
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,196
Messages
2,571,036
Members
47,631
Latest member
kukuh

Latest Threads

Top