Deriving implementation of pure virtual function
Asked Answered
T

3

10

Consider following example

#include <iostream>

struct PureVirtual {
    virtual void Function() = 0;
};

struct FunctionImpl {
    virtual void Function() {
        std::cout << "FunctionImpl::Function()" << std::endl;
    }   
};

struct NonPureVirtual : public FunctionImpl, public PureVirtual {
    using FunctionImpl::Function;
};

int main() {
    NonPureVirtual c;
    c.Function();
}

Compiler (GCC 4.9, Clang 3.5) exits with error

test.cpp:18:20: error: variable type 'NonPureVirtual' is an abstract class
    NonPureVirtual c;
                   ^
test.cpp:4:18: note: unimplemented pure virtual method 'Function' in 'NonPureVirtual'
    virtual void Function() = 0;
                 ^

But when I don't derive form PureVirtual everything is OK. This is weird because Standard 10.4.4 says

A class is abstract if it contains or inherits at least one pure virtual function for which the final overrider is pure virtual.

They are not saying anything about what the final overrider is but I suppose it should be FunctionImpl::Function() especially when I made it available through using directive. So why is still NonPureVirtual abstract class and how can I fix this.

Topgallant answered 12/12, 2013 at 16:8 Comment(12)
PureVirtual is not the base of FunctionImpl, so...Sarge
public FunctionImpl, public PureVirtual -> Function is visible through both classes and specifically from PureVirtual, no virtual inheritance, so it makes it an abstract class. A guess.Dressel
@Simple - I don't think so, or at least - bug in may versions of gcc. I have had the same case, I worked around it, but I don't have the answer here.Kimmy
@Sarge - exactly, that's the case.. The problem is why NonPureVirtual is abstract.Kimmy
@Dressel - I don't think so, you need virtual inheritance only when you have the "diamond inheritance" - this is different case.Kimmy
Nevermind, it's not a bug. I completely misread the question. Obviously NonPureVirtual is not an ABC when you don't have a pure virtual function.Penetrate
The reason it doesn't compile when you inherit from PureVirtual is that you're only affecting the scope/accessibility of FunctionImpl::Function with using; you're not overriding anything.Penetrate
You can do /* virtual */ void Function() { FunctionImpl::Function(); } in NonPureVirtual but I have no idea if this is well defined. Most probably - not.Kimmy
@Kiril Kirov That's the case, and it's the source of the problem.Sarge
@Sarge - the idea is how to make it work, without making PureVirtual base of FunctionImpl ..Kimmy
@KirilKirov I replied to your previous message, sorry. A good idea is in Matthieu M. answer.Sarge
There's a great post on Sutter's Mill/GOTW pertaining to using the override keyword in Overriding Virtual Functions. It also highlights some unexpected pitfalls related to differing default parameters in base and derived classes.Collings
C
11

FunctionImpl::Function and PureVirtual::Function are different functions from different classes.

Their respective types are void (FunctionImpl::*)() and void (PureVirtual::*)().
Since PureVirtual and FunctionImpl are unrelated classes, these function types are unrelated.

They happen to have the same name and the same parameters and return type, but since they're different, the using FunctionImpl::Function line doesn't make that function an override of the one in PureVirtual.

And if you declared a variable of type void (PureVirtual::*)(), you wouldn't be able to assign FunctionImpl::Function to it.

In other words, the final override of PureVirtual::Function is the original one in PureVirtual, which is pure virtual.

For making what you want possible, Matthieu M.'s answer (using a forwarding call) is the way to go, it seems.

Courante answered 12/12, 2013 at 16:26 Comment(9)
Yes, this explains the case, my exaplanation was the same, but "how to fix it?". You can see my comment above: "You can do /* virtual */ void Function() { FunctionImpl::Function(); } in NonPureVirtual but I have no idea if this is well defined. Most probably - not."Kimmy
What I tried, for example, was void PureVirtual::Function() { FunctionImpl::Function(); } but this is not a valid syntax, as expected.Kimmy
@KirilKirov I suspect it could be unfixable, but I'm not sure. Renaming is probably the best way to do it.Courante
Yes, this is how I "worked around" this issue, when I had it. But I still have hope, there's a better way :)Kimmy
@Kiril What about struct NonPureVirtual : ... { void Function() { } }; ... c.FunctionImpl::Function();Fishman
@remyabel - no sure how different this is. Yes, FunctionImpl::Function will be called, but..Kimmy
So how do we distinguish two functions. I've always thought signature is what distinguishes two functions in C++. Those functions obviously have the same signature.Topgallant
@Topgallant I added some more about the function types.Courante
@Topgallant There's more to a signature of a (member) function than just the name and parameter types. It also includes the cv-qualifiers, ref-qualifier and the class the function is member of. The fact that some other class, which PureVirtual and FunctionImpl know nothing about, derives from both of them is irrelevant.Almost
A
5

You cannot use a using directive and have to resort to a fowarding call instead:

struct NonPureVirtual : public FunctionImpl, public PureVirtual {
    virtual void Function() override {
        return FunctionImpl::Function();
    }
};

And yes, it works as expected.

Advent answered 12/12, 2013 at 16:53 Comment(4)
I don't think override changes anything, it's still not clear which base function it does override, or I'm wrong? And I wouldn't rely on a test for such cases.Kimmy
And I don't understand your comment under @suspectus 's deleted answer. I mean - how does your answer solves the ambiguity issue in suspectus?Kimmy
@KirilKirov: override changes nothing, it simply is good form in C++11 to tag as override functions that are in fact overrides; with or without override, NonPureVirtual::Function just overrides both FunctionImpl::Function and PureVirtual::Function. Regarding the ambiguity: there is no ambiguity in @suspectus's answer, however it used copy/paste to reproduce behavior which is to be avoided for maintenance's sake.Advent
+1, I had no idea you could override two functions "simultaneously".Courante
A
2

You're attributing a using declaration with something that it doesn't do. What it does is (From draft n3337, 7.3.3/1):

... using-declaration introduces a name into the declarative region in which the using-declaration appears.

Later from the same paragraph:

If a using-declaration names a constructor (3.4.3.1), it implicitly declares a set of constructors in the class in which the using-declaration appears (12.9); otherwise the name specified in a using-declaration is a synonym for the name of some entity declared elsewhere.

In your case, FunctionImpl::Function is first declared in FunctionImpl class and it is its member. A using-declaration doesn't change that fact, as paragraph 16 says further down:

For the purpose of overload resolution, the functions which are introduced by a using-declaration into a derived class will be treated as though they were members of the derived class. In particular, the implicit this parameter shall be treated as if it were a pointer to the derived class rather than to the base class. This has no effect on the type of the function, and in all other respects the function remains a member of the base class.

Now on to the definition of overriding (10.3/2):

If a virtual member function vf is declared in a class Base and in a class Derived, derived directly or indirectly from Base, a member function vf with the same name, parameter-type-list (8.3.5), cv-qualification, and ref-qualifier (or absence of same) as Base::vf is declared, then Derived::vf is also virtual (whether or not it is so declared) and it overrides Base::vf. For convenience we say that any virtual function overrides itself.

And there's also the definition of a final overrider in the same paragraph.

A virtual member function C::vf of a class object S is a final overrider unless the most derived class (1.8) of which S is a base class subobject (if any) declares or inherits another member function that overrides vf. In a derived class, if a virtual member function of a base class subobject has more than one final overrider the program is ill-formed. [...]

I think that makes it clear that you can't use a using declaration to override a virtual function. A possible fix is in Matthieu's answer.

Almost answered 17/12, 2013 at 10:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.