Delete virtual function from a derived class

24,893

Solution 1

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.

Solution 2

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

Solution 3

"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.

Solution 4

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.

Share:
24,893
Matt Phillips
Author by

Matt Phillips

I create Machine Learning algorithms to solve problems in computer vision, biomedical image understanding, and EHR (electronic health record) understanding, deployed locally or in the cloud (AWS). I'm always happy to enter into new domains, particularly where the data are well-structured. Deep neural networks (CNNs, RNNs, Autoencoders, etc.) are generally my tools of choice. I use PyTorch and Tensorflow/Keras for DL, and Python/PIL/OpenCV for general ML and CV. I also have extensive experience in software engineering (esp. C++) and have loved using it to create products and applications in ML, signal processing, data analysis and visualization. I also have substantial background in experimental and computational neuroscience, which provides good general conceptual tools to help attack my current research and engineering problems. Some of my open-source software includes WaveSorter for sorting and classifying (neural) wavevforms, as well as an application for the convenient visualization of dynamical systems, DynaSys.

Updated on January 26, 2021

Comments

  • Matt Phillips
    Matt Phillips over 3 years

    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?