开发者

Is there a way, using templates, to prevent a class from being derivable in C++

开发者 https://www.devze.com 2022-12-17 12:17 出处:网络
I need to prevent a class from being derived from so I thought to myself, this is something that Boost is bound to have already done. I know they have a noncopyable, they must have a nonderivable...

I need to prevent a class from being derived from so I thought to myself, this is something that Boost is bound to have already done. I know they have a noncopyable, they must have a nonderivable...

Imagine my surprise when I couldn't find it....

That got me thinking.. There must be a reason. Maybe it isn't possible to do using templates..

I'm sure if it was easy it's be in the boost libraries.

I know how to do it without using templates, i.e. using a base class with a private constructor i.e.

class ThatCantBeDerived;  // Forward reference

class _NonDeriv
{
    _NonDeriv() {}
    friend class ThatCantBeDerived;
};

class ThatCantBeDerived : virtual public _NonDeriv
{
public:
    ThatCantBeDerived() :
      _NonDeriv()
    {
    }
};

Or something like this..

Maybe it's the forward reference that causes the problem, or maybe there is开发者_C百科n't a portable way to achieve it..

Either way, I'm not sure why it isn't in boost..

Any ideas?


http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.11


There is no way in C++ of preventing derivation - you cannot prevent this situation:

class A {};

class B : public A {};

However, there are several ways of preventing instantiation of objects of type B. Whether this is worth the trouble is debatable. I would prefer to document that the class A is not intended for derivation, and give it a non-virtual destructor.

Also, note that the name _NonDeriv is reserved in C++ for the implementation, as are all names that begin with an underscore and an uppercase letter. You are not allowed to create such names in your own code.


Under the current spec, it is explicitly forbidden to "friend" a template argument, so templatizing your example would make it not standards compliant. Boost probably would not want to add something like that to its libraries. I believe this restriction is being relaxed in Ox however, and there are workarounds for compilers.


Adobe has a not-perfect solution for this using templates.

The problem there is that since templates cannot declare argument-dependent friends [*] it relies on the constructor being protected instead of private. It is a partial solution in that it will trigger a compiler error when someone mistakenly decides to derive from your class, but it is not a complete solution since someone intentionally can force the inheritance.

template <typename SealedClass>
class seal
{
protected:
   seal() {}
};

class Sealed : private virtual seal<Sealed>
{
//...
};

class NaiveExtension : public Sealed { // fails
   NaiveExtension() {} // NaiveExtension cannot call seal<Sealed> constructor
};

class BreakingExtension : public Sealed, private virtual seal<Sealed> {
   BreakingExtension() : seal<Sealed>(), Sealed() {} // now it can
};

The advantage is that it can be templated (the adobe library actually defines a macro that will hide the 'private virtual' from the casual reader). The disadvantage is that it can be broken.

Then again, you can always do what C++FAQ suggests for some questions on blocking some functionalities:

'how can I inhibit people from...': write a comment not to do it

'but how do I really inhibit others from...': write a comment: You will be fired if...

'but how do I actually block it in case they don't follow the comment?': fire them

[*] This restriction will be taken away with the upcoming standard, whenever available, whenever implemented in compilers...


Easy:

Make all the constructors private:
Then nobdy can derivce from you. Of course this adds othe problems like you can not instanciate a variable of the object, but there are workarounds for that using public static member methods and friends:

#include <memory>
class InstnaceCantDeriveFromMe;
class CantDeriveFromMe
{
    private:
        friend class InstnaceCantDeriveFromMe;
        CantDeriveFromMe()
        {}

    public:
        static std::auto_ptr<CantDeriveFromMe>  getDynamicObj()
        {
            return std::auto_ptr<CantDeriveFromMe>(new CantDeriveFromMe());
        }
};

class Plop: public CantDeriveFromMe
{
};

class InstnaceCantDeriveFromMe
{
    private:
        CantDeriveFromMe  instnace;
    public:
        CantDeriveFromMe& get() {return instnace;}
};

int main()
{
    std::auto_ptr<CantDeriveFromMe>  a =     CantDeriveFromMe::getDynamicObj();
    InstnaceCantDeriveFromMe         b;


    // This fails to compile:
    Plop                             c; 
}


Maybe turn your example into a template, using CRTP - the curiously recurring template pattern:

template <typename T>
_NonDeriv
{
   _NonDeriv() {}
   friend class T;
};

class ThatCantBeDerived : virtual public _NonDeriv<ThatCantBeDerived>
{
public:
    ThatCantBeDerived() :
      _NonDeriv()
    {
    }
};

Might work...

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号