9
  • In c++11 the override specifier protects from not overriding an intended virtual base function (because the signatures do not match).
  • The final specifier protects from unintentionally overriding a function in a derived class.

=> Is there a specifier (something like maybe first or no_override) that protects from overriding an unknown base function?

I'd like to get a compiler error when a virtual function was added to a base class with the same signature as an already existing virtual function in a derived class.


EDIT 4: To keep this question simple and answers relevant, here is again the

original pseudo-code

  • abstract class B : A has private: virtual void fooHasBeenDone() = 0;
  • class C : B implements private: virtual void fooHasBeenDone() override { react(); }
  • Now class A gets a new private: virtual void fooHasBeenDone();
  • But the new A::foo could be something different than the original B::foo.

and a specific example

  • abstract class B : A has virtual void showPath() = 0; meaing a PainterPath
  • class C : B implements virtual void showPath() override { mPath.setVisible(); }
  • Now class A gets a new virtual void showPath(); meaning a file path
  • Now when A calls showPath(), B shows the painterPath instead of some file path.

Of course this is wrong, and I should then rename B::showPath() to B::showPainterPath() and implement B::showPath() override as well. I'd just like to get informed by the compiler.


Here is a compiling real-world example:

#include <iostream>
#define A_WITH_SHOWPATH

class A
{
#ifdef A_WITH_SHOWPATH
public:
    void setPath(std::string const &filepath) {
        std::cout << "File path set to '" << filepath << "'. Display it:\n";
        showPath();
    }
    // to be called from outside, supposed to display file path
    virtual void showPath() {
        std::cout << "Displaying not implemented.\n";
    }
#else
    // has no showPath() function
#endif  
};

class B : public A
{
public:
    virtual void showPath() = 0; // to be called from outside
};

class C1 : public B {
public:
    virtual void showPath() override {
        std::cout << "C1 showing painter path as graphic\n";
    }
};

class C2 : public B {
public:
    virtual void showPath() override {
        std::cout << "C2 showing painter path as widget\n";
    }
};


int main() {
    B* b1 = new C1();
    B* b2 = new C2();

    std::cout << "Should say 'C1 showing painter path as graphic':\n";
    b1->showPath();
    std::cout << "---------------------------\n";
    std::cout << "Should say 'C2 showing painter path as widget':\n";
    b2->showPath();
    std::cout << "---------------------------\n";

#ifdef A_WITH_SHOWPATH
    std::cout << "Should give compiler warning\n or say \"File path set to 'Test'. Display it:\"\n and \"Displaying not implemented.\",\n but not \"C1 showing painter path as graphic\":\n";
    b1->setPath("Test");
    std::cout << "# Calling setPath(\"Test\") on a B pointer now also displays the\n#  PainterPath, which is not the intended behavior.\n";
    std::cout << "# The setPath() function in B should be marked to never override\n#  any function from the base class.\n";
    std::cout << "---------------------------\n";
#endif
    return 0;
}

Run it and look at the text output.


For reference, an older example with a specific use-case (PainterPath instance):

https://ideone.com/6q0cPD (link may be expired)

Martin Hennings
  • 16,418
  • 9
  • 48
  • 68
  • 4
    Maybe `-Wsuggest-override` GCC option may help, see https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html – gavv Jul 29 '16 at 10:27
  • @gavv That's what I am searching for. Is there a platform-independent way? I need it in VS2012, too. – Martin Hennings Jul 29 '16 at 10:28
  • 1
    IMHO this is a question more about design. As Bathsheba mentioned it is not possible except of using final in the base class. Or you can use the trick Leon mentioned, which is breaking the interface class purpose ([see here](http://coliru.stacked-crooked.com/a/17346a88fa6aaa89)) and hase some other issues. As you mentioned in your last edit, you just shouldn't name functions the same with different meanings. if you want to get informend use some analysis tools (also mentioned by Bathsheba imho the only correct answer here). (IMHO) – user1810087 Jul 29 '16 at 11:55
  • @user1810087 I'd accept this as an answer (combining all other answers), should maybe make it community wiki? – Martin Hennings Jul 29 '16 at 11:58
  • 1
    Assuming class A was in a separate header to the deriving class, you could go with something like #define SetPath #error #include "classA.h" #undef SetPath. That would prevent any SetPath function being added to A, but would build without error until SetPath was added to A. I'm not saying its nice. If I put it as an answer I'd expect some people to downvote it purely for its use of #define. – ROX Jul 29 '16 at 12:01
  • 1
    You have created more confusion with the new code! :-) Everytime you change the code, it invalidates the examples taken from your Q in our answers. Why do you use term "instance of `B`"? It confuses, as `B` cannot have instance due to pure `virtual`. Also, what `PainterPath` has to do here? Your last statement is confusing. I suggest that instead of putting the actual code with constructors etc., just put the minimal part, which is independent & anyone can verify using ideone.com. You can also look at my answer & comment under it, if it doesn't satisfy what you need. – iammilind Jul 29 '16 at 12:14
  • 2
    @iammilind _"Why do you use term "instance of B"? It confuses, as B cannot have instance due to pure virtual"_ AFAIU in OO generally an instance of a subclass is also an instance of the base class. Read "instance of B" as "instance of B (including any classes derived from B)". – davmac Jul 29 '16 at 13:31
  • @iammilind *"You have created more confusion with the new code"* I was afraid that might happen, but my first examples were not clear enough. Thanks for the hint though. I created a compiling example as you suggested. – Martin Hennings Jul 29 '16 at 15:48
  • Try to still reduce your example as it's too time consuming to go through & paste it in your question. The ideone.com links are temporary & once they go off, there won't be any trace of the code. Now when I reviewed the code, I found it to be properly functioning. Probably what you want is that a compiler error to notify that `B::showPath()` is having a same signature as `A::showPath()` & hence do something with it. Well, as I have wrote in my answer, you may mark `A::showPath()` as `final` while adding it & compile the code. Fix any errors & then remove `final` for the production. – iammilind Jul 30 '16 at 06:24

4 Answers4

3

The facility of specifiers like first or no_override is not there as such. Probably because it may create confusion. However, it can trivially be achieved by changing the approach.

One should add any new method in the base class with final specifier. This will help to get the compiler error for any matching signatures. Because, it will make the subsequent derived class method signatures automatically as "first" of their kind. Later the final keyword can be removed, as it was intended just for "first hand verification".

Putting & removing final keyword after the newly added base method is analogically similar to compiling binary with debug (g++ -g) option, which helps you to fix bug. In production that debug option is removed for optimization.

From your example:

class A {};  // no method, no worry

class B {
  public: virtual void showPath() = 0;  // ok
};
...

Now accidentally you are adding similar method in A, that results in error:

class A {
  public: virtual void showPath() final;  // same signature by chance
  // remove the `final` specifier once the signature is negotiated
}; 
class B {
  public: virtual void showPath() = 0;  // ERROR
};

So the signatures between new A::showPath() & existing B::showPath() have to be negotiated & then carry on by removing final specifier.

iammilind
  • 68,093
  • 33
  • 169
  • 336
  • 1
    @user1810087, the OP wants to add the new method in the base class and intend to get a compiler error, if it's accidentally same as any of the derived classes. So when we add the new method with `final` specifier, the subsequent signature of derived class automatically becomes "first" of its kind. If it's same, then compiler gives error. – iammilind Jul 29 '16 at 10:54
  • no, he want's to get a compiler error when a method **was** added to the base, not if **he adds** a method to the base. – user1810087 Jul 29 '16 at 10:56
  • @user1810087, whoever adds the method in the base - be it OP or other coder, must be adding it with `final` specifier, if they care about the subsequent derivations. If they intend to add the method to be overridden, then anyways, `final` is not required and consequences of matching signature has to be negotiated. – iammilind Jul 29 '16 at 10:59
  • 2
    I'd agree that use of final is generally the way to do this - making the base class responsible for the check, but the OP is aware of final and appears to be asking for an alternative method that doesn't require the base to use final, and put responsibility on the derived class instead. – ROX Jul 29 '16 at 11:07
  • 1
    @ROX, actually I have edited my answer just now. The `final` keyword is not to be put permanently. It's meant only to make sure that: "I am adding a new method & I don't want to manually check in all derived classes if any such method is existing, so give me quick report automatically." Analogically it's something like compiling code in g++ with `-g` option where you need to debug the stacktrace. Once debugged and bugs are fixed, we remove the `-g` and recompile the code. – iammilind Jul 29 '16 at 11:10
  • but now B::fooHasBeenDone() is not implemented and not useable. – user1810087 Jul 29 '16 at 11:14
3

This answer is community wiki because it combines all other answers. Please upvote the specific answer that was helpful to you as well as this one.

  1. No, there is no specifier like first or no_override. (answer)
  2. You should use the override specifier as often as possible.
    Qt has a macro Q_DECL_OVERRIDE that expands to override, if available.
    If not available, at least mark each overriding function with a comment.
  3. If you do that, there are compiler flags that warn about a missing override:
    "Clang now has -Winconsistent-missing-override, and newer GCCs have -Wsuggest-override."
    I don't know of a VS2012 flag for this. Feel free to edit.
  4. You can mimic the desired behavior by adding a 'secret' that the base class cannot know. (answer)
    This is helpful in very specific use cases, but generally breaks the concept of virtuality (see comments to the other answers).
  5. If you don't own the base class and have a conflict (e.g. compiler warning), you will need to rename your virtual function in all derived classes.
  6. If you own the base class, you can temporarily add a final to any new virtual function. (answer)
    After the code compiles without errors, you know that no function of that name and signature exists in any derived class, and you can remove the final again.

... I think I'll start marking first virtual functions as DECL_FIRST. Maybe in the future there will be a compiler-independent way of checking this.

Community
  • 1
  • 1
Martin Hennings
  • 16,418
  • 9
  • 48
  • 68
2

No there is not.

Adding a virtual function to a base class that has the same signature as a virtual function in a child class cannot break any existing functionality unless adding that virtual function turns the base class into a polymorphic type. So in the norm, it's benign, and a purest would argue, adding language features to guard against this would be rather pointless.

(Of course you could mark your new function final just to check that a child class function isn't going to clobber it.)

Your only option is to resort to code analysis tools.

(Note that VS2012 does not implement, or even claim to implement, the C++11 standard, although it does have some of it.)

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • 2
    How can it not break existing functionality? The overloaded virtual function might be executed in unexpected / invalid situations. – Martin Hennings Jul 29 '16 at 10:33
  • 2
    When? I can't think of a situation. Do submit a counter-example (along with a downvote naturally). It might cure my hangover. – Bathsheba Jul 29 '16 at 10:34
  • @Martin: There's no way to invoke an arbitrary "least derived" function as far as I'm aware. You'd have to qualify the name (e.g. `Base::foo()`) and then you already must know the function exists. – Lightness Races in Orbit Jul 29 '16 at 10:40
  • 1
    @LightnessRacesinOrbit See my edit - Base might call foo() in places Derived doesn't expect. (If Base isn't mine, I'd want to rename Derived::foo() to resolve the conflict.) – Martin Hennings Jul 29 '16 at 10:42
  • @Martin: If the Base calls `foo()` and it now has a virtual `foo()` then the behaviour is unchanged. If the Base calls `foo()` and it now has a non-virtual `foo()`, then it would not have compiled originally anyway. Either way, adding `foo()` to the Base cannot change behaviour (unless we count "the program now compiles" as changing behaviour ;P) – Lightness Races in Orbit Jul 29 '16 at 10:46
  • @LightnessRacesinOrbit I specified my example. Does that answer your question? – Martin Hennings Jul 29 '16 at 11:07
  • @Martin: It's giving me a headache. Can you provide an _actual_ code example? – Lightness Races in Orbit Jul 29 '16 at 11:14
  • @Martin: _"Now when A calls showPath(), B shows the painterPath instead of some file path."_ So what? Previously, when A called showPath(), the program failed to compile. So what's the "changing behaviour"? (Even if there were any, and if you are _in_ A changing what A is and does, then of course you expect A's behaviour to change. This is not affecting your more-derived classes or your end user.) – Lightness Races in Orbit Jul 29 '16 at 11:15
  • 3
    @Bathsheba *Do submit a counter-example* See http://stackoverflow.com/questions/38657534/is-it-possible-to-break-code-by-adding-a-new-virtual-function-in-the-base-class – Leon Jul 29 '16 at 11:20
  • @Leon: I reject that as a counter-example since it's more to do with DLL theory than C++ itself. – Bathsheba Jul 29 '16 at 11:53
  • 1
    @LightnessRacesinOrbit http://stackoverflow.com/a/38658152/981959 compiles with or without the virtual in the base class, but changes behaviour. – Jonathan Wakely Jul 29 '16 at 11:54
  • @Bathsheba See my answer: http://stackoverflow.com/a/38657535/6394138 The DLL related answer was added by someone else and received more upvotes for an unknown reason – Leon Jul 29 '16 at 11:56
  • @Leon: I concede. Well done. I've rather sluttily added a new sentence to my answer. – Bathsheba Jul 29 '16 at 12:02
  • 1
    @Bathsheba, the base class could already be polymorphic and you still have the same problem. Other, unrelated virtual functions in the base makes no difference to Leon's example (which mine is very similar to) – Jonathan Wakely Jul 29 '16 at 12:04
  • @JonathanWakely: The OP's example had `virtual void foo` in `B`, so that's not equivalent. Though I suppose that's moot as your [different] example is a sound one. – Lightness Races in Orbit Jul 29 '16 at 12:05
  • @Bathsheba Thanks. Note that I didn't downvote your answer, but will upvote it after you fix it. – Leon Jul 29 '16 at 12:10
1

C++ doesn't seem to provide such means out of the box. But you can mimic it like follows:

template<class Base>
class Derived : public Base
{
private:
    struct DontOverride {};

public:
    // This function will never override a function from Base
    void foo(DontOverride dummy = DontOverride())
    {
    }
};

If you intend to introduce a new virtual function, then do it like below:

template<class Base>
class Derived : public Base
{
protected:
    struct NewVirtualFunction {};

public:
    // This function will never override a function from Base
    // but can be overriden by subclasses of Derived
    virtual void foo(NewVirtualFunction dummy = NewVirtualFunction())
    {
    }
};
Leon
  • 31,443
  • 4
  • 72
  • 97
  • 1
    This has potential but `foo` needs to be virtual. Perhaps make the struct `protected`? `protected`ness works "downwards" not "upwards" if you get my meaning. – Bathsheba Jul 29 '16 at 10:37
  • `// This function will never override a function from Base` , because it cannot ever be virtual in the base class! – user1810087 Jul 29 '16 at 10:38
  • @user1810087 It CAN be virtual, but then DontOverride must be made protected, so that subclasses can override it. – Leon Jul 29 '16 at 10:39
  • virtual in the base class? could you show an example? – user1810087 Jul 29 '16 at 10:40
  • Nice idea, and not much more boilerplate code than a specifier. – Martin Hennings Jul 29 '16 at 10:40
  • 3
    Although a foo function with the same signature cannot exist in the base, so Derived::foo doesn't override any base function, giving the dummy parameter a default value does mean that a call to Derived::foo() without parameters would be a call to Derived::foo where a call to Base::foo() may have been intended - foo also being a new virtual function added to Base, with (in this example) no parameters. The overall effect being still being that the new base function doesn't get called. – ROX Jul 29 '16 at 11:00
  • 1
    i'm with @ROX here. Derived::foo hides the overloaded function Base::foo, which tend the call of foo (over a Base-pointer pointing to Derived) will call Base::foo, while calling Derived::foo need's to be a Derived pointer, which brakes the entire purpose of interface classes. – user1810087 Jul 29 '16 at 11:22