39

I have a virtual base class function which should never be used in a particular derived class. Is there a way to 'delete' it? I can of course just give it an empty definition but I would rather make its attempted use throw a compile-time error. The C++11 delete specifier seems like what I would want, but

class B
{
    virtual void f();
};

class D : public B
{
    virtual void f() = delete; //Error
};

won't compile; gcc, at least, explicitly won't let me delete a function that has a non-deleted base version. Is there another way to get the same functionality?

Matt Phillips
  • 9,465
  • 8
  • 44
  • 75
  • 1
    How is it possible? Do you want compiler error in `D *d = new D(); static_cast(d)->f()`? – Bryan Chen Jul 07 '14 at 11:49
  • 1
    @BryanChen If it's cast to pointer to base class then it should act like a pointer to base class--in which case `D::f` would still get called, so yes. – Matt Phillips Jul 07 '14 at 11:53
  • And what behaviour would you expect in that case then? – Theolodis Jul 07 '14 at 11:54
  • @Theolodis Changed my answer to Bryan Chen's question, forgot for a moment that derived class functions still get called after casting a pointer to base class. – Matt Phillips Jul 07 '14 at 11:57
  • I think you misunderstood the use of the `delete` keyword. The `delete` keyword is not meant to remove method from child class, which would break rules of inheritance, it should be used to disable specific call (such as copy call or call from specific type of parameters). – Holt Jul 07 '14 at 11:58
  • @Holt From the question, "Is there another way to get the same functionality?" I don't really care about being able to use `delete` per se, I am just interested if it is possible to get a compile-time error associated with use of a derived class function that is inherited from the base class. – Matt Phillips Jul 07 '14 at 11:59
  • @MattPhillips Have you tried implementing the child-class virtual function and placing a `static_assert` in it? – KitsuneYMG Jul 07 '14 at 13:17
  • @KitsuneYMG Theolodis tried that (look through the edit history of his question) but it is too strong, it throws an error whether the function is called or not. – Matt Phillips Jul 07 '14 at 13:25

6 Answers6

55

It is not allowed by the standard, however you could use one of the following two workarounds to get a similar behaviour.

The first would be to use using to change the visibility of the method to private, thus preventing others from using it. The problem with that solution is, that calling the method on a pointer of the super-class does not result in a compilation error.

class B
{
public:
    virtual void f();
};

class D : public B
{
private:
    using B::f;
};

The best solution I have found so far to get a compile-time error when calling Ds method is by using a static_assert with a generic struct that inherits from false_type. As long as noone ever calls the method, the struct stays undefied and the static_assert won't fail.

If the method is called however, the struct is defined and its value is false, so the static_assert fails.

If the method is not called, but you try to call it on a pointer of the super class, then Ds method is not defined and you get an undefined reference compilation error.

template <typename T>
struct fail : std::false_type 
{
};

class B
{
public:
    virtual void f() 
    {
    }
};

class D : public B
{
public:
    template<typename T = bool>
    void
    f()
    {
        static_assert (fail<T>::value, "Do not use!");
    }
};

Another workaround would be to throw an exception when the method is used, but that would only throw up on run-time.

Theolodis
  • 4,977
  • 3
  • 34
  • 53
  • 1
    Nice, but `B* d = new D(); d->f();` will run without error (and `B::f` will get called), but this is the kind of situation I would want an error to occur. – Matt Phillips Jul 07 '14 at 12:12
  • @MattPhillips as you stated in your comments that behaviour would have been expected anyway, or am I wrong? – Theolodis Jul 07 '14 at 12:17
  • No, I would never want the base class version called. My answer to Bryan Chen was updated after you answered it, I would want a compiler error in this case. – Matt Phillips Jul 07 '14 at 12:26
  • I tested out your last solution [here](http://ideone.com/JW0uKU), unfortunately it appears to lead to an error whether `f` is called or not. – Matt Phillips Jul 07 '14 at 12:43
  • @MattPhillips I reverted my suggestion, however if your virtual method is not pure virtual there is no chance that `D`s method is being called on a `B` pointer anyway, right? – Theolodis Jul 07 '14 at 13:58
  • I don't know what you mean. The base class in my particular scenario is indeed abstract, so `new B()` can never happen, but my question doesn't really depend on that. – Matt Phillips Jul 07 '14 at 14:04
  • Hey this version looks cool but where is `fail` defined? I've `#include`d type_traits and functional, but g++4.9 even isn't seeing it. – Matt Phillips Jul 07 '14 at 16:03
  • Ok I had to define `fail` myself--you may wish to edit that in to your answer for future seekers. Anyway, BRILLIANT! Thanks. – Matt Phillips Jul 07 '14 at 16:07
  • @MattPhillips actually `fail` is already declared and defined in my sample ;) see the empty body (`{}`) – Theolodis Jul 07 '14 at 16:14
  • Ah I see, missed that. So then don't change a thing. :) – Matt Phillips Jul 07 '14 at 16:15
  • @Theolodis Why don't you just pass "false" to static_assert instead of all that "fail" mambo jumbo? – Zingam Dec 15 '17 at 11:51
  • 2
    @Zingam if you put in `static_assert(false);` it would never compile. The template struct is only initialized if the method is used. – Theolodis Dec 15 '17 at 12:15
  • @Theolodis I think it does. I used your example to modify this example and it appears to work correctly it compiles and runs and delivers what's promized. You can check it here if you like: https://ufile.io/rxetj Note: Compiler is VS2017 – Zingam Dec 15 '17 at 12:51
  • If you put in a static_assert with false, it will never compile. It can only work if you put the code in a separate header file that noone uses. My solution however lets you use B's f() method without restrictions and only fails to compile if you use D's f() method in your code. You're welcome to prove me wrong with a http://cpp.sh or similar minimal example though. – Theolodis Dec 15 '17 at 13:52
15

The standard does not allow you to delete any member of a base-class in a derived class for good reason:
Doing so breaks inheritance, specifically the "is-a" relationship.

For related reasons, it does not allow a derived class to define a function deleted in the base-class:
The hook is not any longer part of the base-class contract, and thus it stops you from relying on previous guarantees which no longer hold.

If you want to get tricky, you can force an error, but it will have to be link-time instead of compile-time:
Declare the member function but don't ever define it (This is not 100% guaranteed to work for virtual functions though).
Better also take a look at the GCC deprecated attribute for earlier warnings __attribute__ ((deprecated)).
For details and similar MS magic: C++ mark as deprecated

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
  • Ok +1 for the link-time error suggestion. I don't care about using `delete` specifically, I just want the functionality. – Matt Phillips Jul 07 '14 at 12:01
  • Actually, doing that produces an 'undefined vtable' error even if `f` is never called, as shown [here](http://ideone.com/JW0uKU). – Matt Phillips Jul 07 '14 at 15:54
  • @MattPhillips: Some compilers/linkers don't complain if the implementation of some virtual method isn't there. Sometimes depending on compiler options. Still, the best bet is always deprecation. – Deduplicator Jul 07 '14 at 16:11
4

"I have a virtual base class function which should never be used in a particular derived class."

In some respects that is a contradiction. The whole point of virtual functions is to provide different implementations of the contract provided by the base class. What you are trying to do is break the contract. The C++ language is designed to prevent you from doing that. This is why it forces you to implement pure virtual functions when you instantiate an object. And that is why it won't let you delete part of the contract.

What is happening is a good thing. It is probably preventing you from implementing an inappropriate design choice.

However:

Sometimes it can be appropriate to have a blank implementation that does nothing:

void MyClass::my_virtual_function()
{
    // nothing here
}

Or a blank implementation that returns a "failed" status:

bool MyClass::my_virtual_function()
{
    return false;
}

It all depends what you are trying to do. Perhaps if you could give more information as to what you are trying to achieve someone can point you in the right direction.

EDIT

If you think about it, to avoid calling the function for a specific derived type, the caller would need to know what type it is calling. The whole point of calling a base class reference/pointer is that you don't know which derived type will receive the call.

Galik
  • 47,303
  • 4
  • 80
  • 117
  • 2
    "The whole point of calling a base class ..." This is right for the general case, but not always; if it was always true the c++ language would not contain `static_cast` and `dynamic_cast`. Sometimes the derived class identity matters for the calling code and this one of those times. – Matt Phillips Jul 07 '14 at 12:39
  • It is definitely sometimes important to deal directly with the derived class. This is indeed what dynamic_cast is used for. But then you are no longer calling the base class reference/pointer so my statement doesn't really apply. But I hoped it would answer your question why its not allowed. – Galik Jul 07 '14 at 13:18
2

What you can do is simply throwing an exception in the derived implementation. For example, the Java Collections framework does this quite excessively: When an update operation is performed on a collection that is immutable, the corresponding method simply throws an UnsupportedOperationException. You can do the same in C++.

Of course, this will show a malicious use of the function only at runtime; not at compile time. However, with virtual methods, you are unable to catch such errors at compile time anyway because of polymorphism. E.g.:

B* b = new D();
b.f();

Here, you store a D in a B* variable. So, even if there was a way to tell the compiler that you are not allowed to call f on a D, the compiler would be unable to report this error here, because it only sees B.

gexicide
  • 38,535
  • 21
  • 92
  • 152
1

Instead of getting tricky, why not just use proper inheritance:

class B { 
  // B is the generalization that provides common members
};

// Then you define appropriate specializations of B to derive from
class C1: public B {};
class C2: public B { 
  virtual void f() = 0;
};

class D: public C1 {
  // Inherits from B, and doesn't inherit void f()
};
ardnew
  • 2,028
  • 20
  • 29
0

I have a virtual base class function which should never be used in a particular derived class.

C++11 provides a keyword final which prevents a virtual function being overriden from.

Look: http://en.cppreference.com/w/cpp/language/final .

class B
{
  virtual void f() final;
};

class D : public B
{
  // virtual void f();  // a compile-time error
  // void f() override; // a compile-time error
  void f(); // non-virtual function, it's ok
};
sergs
  • 123
  • 1
  • 12