17

C++11 introduced the keyword final to forbid future overrides or to prohibit inheritance. The most common example where one may use it is for the case of classes that are not intended to be used as base classes (have e.g. non-virtual destructors). However, sometime we may want to have an is-implemented-in-terms-of relation between two classes (i.e. private inheritance), and not a is-a relationship (public inheritance). However, final prohibits both types of inheritance.

My question is the following: is there any way of allowing private inheritance but forbidding public inheritance (probably not directly, but at least can we "simulate" it)? In this case, there won't be any issues even if we use a class with a non-virtual destructor, as we cannot use directly the derived class via a pointer to base, so we should be fine.

I am thinking of a code like this:

class Base /*final*/ {}; // making it final prohibits both private and public inheritance

class PrivateDerived: private Base{}; // this should work

class PublicDerived: public Base{}; // this shouldn't

int main()
{
    PrivateDerived prvd;
    PublicDerived  pubd; // this should not compile

    // Base* pBase = new PrivateDerived; // doesn't work, so we are ok
}
vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • Protected default (other) constructors. –  May 04 '15 at 19:26
  • @DieterLücking Or destructors, but that might come out more tricky. – πάντα ῥεῖ May 04 '15 at 19:28
  • 2
    @DieterLücking even if I make the ctor in `Base` protected, the line `class PublicDerived: public Base{};` still compiles, since the derived classes "know" how to construct the base. In fact, the whole program compiles. And also the problematic `Base* pBase = new PrivateDerived;` will still be ok. – vsoftco May 04 '15 at 19:30
  • @vsoftco That just came to my mind while I was to write an answer going into this direction. – πάντα ῥεῖ May 04 '15 at 19:32
  • 3
    Is this an XY-question ? –  May 04 '15 at 19:32
  • @DieterLücking not really, I was just thinking why `std::string` is not marked `final` starting with C++11, but probably because it may break some code that derives privately from `std::string` (public derivation is just plain wrong, use it via a pointer and all bets are off). Then I was thinking whether we can allow this kind of derivation but forbid public one. – vsoftco May 04 '15 at 19:34
  • Why not make "Base" a private member variable? – Mustafa Ozturk May 04 '15 at 19:35
  • @MustafaOzturk because I want to be able to use it as a standalone class, exactly like `std::string`. – vsoftco May 04 '15 at 19:36
  • 1
    I've got something lurking in my backup-brain regarding refactoring `friend` interfaces, and use `protected` construction solely, but that's too vague now for giving an answer and requires another level of decoupling than a simple inheritance. @MustafaOzturk proposal goes in that direction, though I certainly mean a reference. – πάντα ῥεῖ May 04 '15 at 19:37
  • Why do you want PrivateDerived to be derived from Base as "private"? What benefit does it give you instead of having Base as a private member variable of the class PrivateDerived? – Mustafa Ozturk May 04 '15 at 19:37
  • @vsoftco Deriving from std::string puts you already in a lot of trouble (just imagine all operators and free standing functions involved) –  May 04 '15 at 19:38
  • @MustafaOzturk as I said, it models a **implemented-in-terms-of** relation. Think for example of the "Adapter" design pattern, where private inheritance is the most natural thing to do (you gain private access to the member functions of the Base). I agree you can "simulate" some stuff via a private member variable, but private inheritance is (imo) more natural sometime. – vsoftco May 04 '15 at 19:39
  • @vsoftco Well, I found my backup, I have already posted what I had in mind [here](http://stackoverflow.com/questions/27492132/how-can-i-remove-refactor-a-friend-dependency-declaration-properly). I'm not sure if this is appropriate to lead you into the right direction. – πάντα ῥεῖ May 04 '15 at 19:47
  • If I get it right: This would require to implement a std::string (or other class) interface, but C++ does not provide it (not making any assumption, whether a generic interface is useful or not). –  May 04 '15 at 19:47
  • @πάνταῥεῖ thanks for the link, it looks quite substantial :) I'll munch it a bit and let you know if it works. – vsoftco May 04 '15 at 19:49
  • @vsoftco Making `std::string` a `final` class could break any code that inherits from `std::string`, regardless of the type of inheritance. – juanchopanza May 04 '15 at 19:49
  • @juanchopanza yes, I realized that, and that's why I was asking how can we forbid public inheritance but allow private one. Inheriting publicly from `std::string` is plain wrong imo, since it is just a disaster waiting to happen (usage via base pointers). – vsoftco May 04 '15 at 19:51
  • @vsoftco Well, I think my backed up mindings just came up because I've been trying to realize that `protected` constraint from the UML. – πάντα ῥεῖ May 04 '15 at 19:52
  • @vsoftco But disallowing public inheritance from `std::string` would still have been a breaking change, and C++ doesn't like breaking backwards compatibility. – juanchopanza May 04 '15 at 20:00
  • A CRTP base class template could `static_assert` on `is_convertible`, I suppose. As far as I know, there's not even a way to detect whether something is a public base (that is, including ambiguous public bases). – T.C. May 04 '15 at 20:08
  • protected constructors/destructors with a CRTP friend that inherits from you. That CRTP friend uses decltype to check itself is a private base, but not a public base, of its template parameter. It friends its CTRP parameter, and itself as protected constructors/destructors? Or something like that? Bit a mess. `struct derived: private secret_string` sort of think in the end-class. – Yakk - Adam Nevraumont May 04 '15 at 20:18

2 Answers2

5

Interesting question! If you don't mind giving up the destructor's triviality, I think the following does the job:

#include <type_traits>

template <typename T>
class Base {
protected:
    ~Base() {
        static_assert(!std::is_convertible<T*,Base*>::value, "Invalid use of public inheritance.");
    }    
};

class Derived : public Base<Derived> {
};

int main() {
    Derived d;
}

The code above fails to compile: the static_assert fires because Derived* is convertible to Base<Derived>*. However if you change the inheritance to either protected or private then the code compiles.

Unfortunately users can still shoot themselves in the foot:

class Bad : public Base<Derived> {
};
Cassio Neri
  • 19,583
  • 7
  • 46
  • 68
  • @JerryJeremiah If `Derived` derives publicly from `Base` then `std::is_convertible*>::value == true`. Therefore the negation is `false` and the `static_assert` fires. However, if the inheritance is `private` then `std::is_convertible*>::value == false`. – Cassio Neri May 05 '15 at 10:55
  • @CassioNeri thanks, this seem to do the job indeed! I wonder just how "safe" is to use `std::is_convertible` when dealing e.g. with multiple inheritance... but still, let's not overcomplicate the problem :) (in particular, why did you use `is_convertible` and not `is_base_of`? Ohh I see, `is_base_of` returns true for any kind of inheritance, not just `public`.) Also, why are you saying you must have a trivial destructor in `Base`? It seems I can add stuff to it without any problems, after the static assertion. – vsoftco May 05 '15 at 14:15
  • @vsoftco Yes, you can add stuff to the destructor. My point is that because you define the destructor yourself the compiler won't consider it trivial. So there might be a cost in defining the destructor as above. If you must declare a destructor for other reasons, then the `static_assert` has "no cost". If `Base` has constructors you can alternatively place the `static_assert` in *all* of them to get the same effect (paying the price of code duplication if there's more than one constructor). – Cassio Neri May 05 '15 at 14:46
0

I am not sure if this is what you are looking for or if this will help you in your case. I will however demonstrate polymorphic behavior.

Protected Constructor Abstract Class

class BaseProtected {
// ----- Member Variable Section -----
public: 
    // There Shouldn't Be Public Variables In A Base Class Unless 
    // That Is The Behavior You Are Looking For - Keep In Mind
    // Every Inherited Class & Outside Class Can Change Them.
protected:
    // Member Variables Here To Be Shared With Each Derived Class
private:
    // Member Variables Here To Be Used By Base Class Only

// ----- Member Function Section -----
public:
    virtual ~BaseProtected(); // Virtual Destructor 
    void somefunc() const; // Common Function Between All Derived Class
    virtual void allDerivedMustImplement() const; = 0 // Purely Virtual  

protected:
    // Default Constructor - Can Not Declare An Instance Of Base Class
    BaseProtected(); // Abstract Class         

   // Protected Functions Shared Between Classes
   // Protected Functions That Are Purely Virtual If Needed

private:
    // Private Functions Used By Base Class Only

}; // BaseProtected

Derived Class With Possible Inheritance

class DerivedWithPossibleInheritance : public BaseProtected {
// ----- Member Variable Section -----
public:
    // Public Member If Giving Free Access   
protected:
    // Protected Members If Being Inherited From
private:
    // Private Members Unique To This Derived Class

// ----- Member Function Section ----- 
public:
    DerivedWithPossibleInheritance(); // Default Constructor
    virtual ~DerivedWithPossibleInheritance(); // Virtual Destructor

    void uniqueFunctionForThisClass() const;
    void allDerivedMustImplement() const override;

private:
    // Private Functions Unique To This Class

}; // DerivedWithPossibleInheritance 

Derived Class That Can Not Be Inherited From

class DerivedClassCanNotBeInheritedFrom sealed : public BaseProtected {
// ----- Member Variable Section -----
public: 
    // Public Members Variables
protected:
    // Should Not Have Member Variables Here For This Class Can Not Be Inherited from
private:
    // Private Members Variables

// ----- Member Function Section ------
public:
    DerivedClassCanNotBeInheritedFrom(); // Default Constructor
    virtual ~DerivedClassCanNotBeInheritedFrom(); // Default Virtual Destructor

    void anotherUniqueFunctionForThisClass() const;
    void allDerivedMustImplement() const override;

protected:
    // There Should Not Be Any Functions Here This Can Not Be Inherited From

private:    
    // Private Member Functions Here

}; // DerivedClassCanNotBeInheritedFrom

What I have demonstrated here is the key word sealed when working with inheritance and polymorphism. If you do not want your class to be derived from then use the sealed keyword.

As for declaring any base class, a stand alone class, or a singleton class that has a private Constructor the use of the friend keyword is needed. There are to many types of implementations to get involved with here to show them, but the concept of preventing a class from being inherited from is the same. Personally I have not used the keyword final, but I have used the keyword sealed and it works very good.

I have not used inheritance of classes other then public: So to answer your questions in terms of protected or private inheritance is not something I am real familiar with. But maybe the use of the keyword sealed might help you.

user207421
  • 305,947
  • 44
  • 307
  • 483
Francis Cugler
  • 7,788
  • 2
  • 28
  • 59
  • Thanks for the effort, but unfortunately `sealed` is a compiler extension (if I'm not wrong it works only on VS), and more than that, C++11 introduced `final` as a standard keyword to prevent inheritance (basically the same effect as `sealed`). I want however to prevent public inheritance, but allow private/protected inheritance. – vsoftco May 05 '15 at 03:15
  • Okay I wasn't 100% sure but yes I am working with VS2012 & VS2013 – Francis Cugler May 05 '15 at 03:24
  • And yes I do understand your question but I never had a scenario come up for that kind of use therefor to answer you question is a little bit beyond my own skills. – Francis Cugler May 05 '15 at 03:25