0

This has the feeling of a complete newbie question, but why does the following code not compile when the final specifier is used for B::operator()?

struct A
{
    virtual void operator()() const = 0;
};

// the CRTP-component is not really necessary here
// but it possibly makes more sense in that it could be applied like this in reality
//
template<typename Derived>
struct B : A
{
    virtual void operator()() const override final
    {
        static_cast<Derived const&>(*this).operator()();
    }
};

struct C : B<C>
{
    void operator()() const
    {
        //do something
    }
};

int main()
{
    C()();
}

G++ prints the following error message:

main.cpp:17:14: error: virtual function 'virtual void C::operator()() const'
         void operator()() const
              ^
main.cpp:9:22: error: overriding final function 'void B<Derived>::operator()() const [with Derived = C]'
         virtual void operator()() const override final
                      ^

I would have thought it works as the non-virtual C::operator() does not override the virtual functions in its base classes? How can I bring this to work (--without changing the name of C::operator())?


EDIT: As pointed out by several users, the answer is simply that the virtual-keyword in the derived class is redundant (whereas I thought leaving it out would prevent from inheriting). However, the goal I had in asking this -- namely a consistent interface throughout the dynamic and static inheritance hierarchy -- can be solved by using a non-virtual operator[] throughout and couple classes A and B by a virtual function apply:

struct A
{
    void operator()() const
    {
        this->apply();
    }

protected:
    virtual void apply() const = 0;
};

template<typename Derived>
struct B : A
{
    void operator()() const
    {
        static_cast<Derived const&>(*this).operator()();
    }

protected:
    virtual void apply() const override final
    {
        this->operator()();
    }
};

struct C : B<C>
{
    void operator()() const
    {
        //do something
    }
};

int main()
{
    C()();
}
2
  • @Cheersandhth.-Alf: I was thinking C::operator() does only override when the virtual-keyword is used ... which is wrong as I learned today. Commented Mar 16, 2015 at 21:12
  • Oh, okay. I changed my answer, removing question about that. I guess the question now is how someone can have learned about references and final and override and virtual and template, and not about overrides. Commented Mar 16, 2015 at 21:15

3 Answers 3

7

If a function is declared virtual in a base class, then a function declared with the same name and parameter list is implicitly virtual in derived classes, whether or not you use the virtual keyword. You cannot make C::operator()() non-virtual.

Sign up to request clarification or add additional context in comments.

Comments

4

A function in a derived class with the same signature as a virtual function in a base class overrides that virtual function from the base class. That makes it a virtual function, even if/though the declaration in the derived class doesn't use the virtual key word.

That can't be changed, so if you really need to have a function with the same name in a derived class that doesn't override the virtual function from the base class (and in the process, become virtual itself and in this case, violate the final in B) you'll need to change the signature of the function in the derived class. That can mean a different name, different parameter list, or different qualifiers. I'd treat the latter two with extreme caution though--the compiler will be able to sort out the mess you've made, but many human readers may (very easily) be surprised.

If I were reviewing such code, I'd probably cite this as a problem, and the author would need to provide very solid reasoning for why it was truly necessary to get it approved.

6 Comments

Thanks. I agree, changing the parameters and qualifiers is a mess. Still, that there is no way to prevent from overriding (maybe by simply dropping virtual as I incorrectly thought) is a bit unfortunate imo, as it means one cannot mix static- and dynamic-polymorphism using common function signatures throughout. But that might be subjective, as I use this pattern very often (and from now on hopefully more correct :-D)
My last statement isn't true. One can use a non-virtual operator[] throughout and couple the dynamically-connected ase and derived classes via an `apply-function. I make an edit in my original question.
@davidhigh: You can do this, but it strikes me as a design smell. The derived operators are hiding the base operator instead of overriding it, so if (for example) you call via a pointer to base, you'll get different behavior than you really want. Overall, the inheritance is saying "A C is an A", but now you're going back and saying, "but not really an A".
Thanks for your hints! So you would prefer the first variant in the OP with an apply function instead of operator() in the static-polymorphism branch? Still I can't figure out where exactly the second alternative is smelly (see also this short FAQ entry) ... imo it satisfies the requirement that the client always sees the same behaviour (also when called via base class)...?
@davidhigh: I'd probably have to look at real code to be sure I understand what you're getting at, but my impression was that you were trying to get different behavior in C depending on whether you invoked it via a pointer/reference to A, or directly to an object of type C.
|
1

As an override (because it has the same signature as the virtual function in a base class), the override conflicts with the final specified in its base class.

One fix (or rather workaround) is to give that function a defaulted argument, so that it has a different type and hence not an override, and a better approach is to fix the design.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.