tags:

views:

240

answers:

6

Hi,

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 isn't a portable way to achieve it..

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

Any ideas?

+7  A: 

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

Kevin
very cool link.
Anna
+2  A: 

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.

Todd Gardner
To be fair, the vptr tax mentioned in the c++ faq lite is probably also a very good reason
Todd Gardner
+3  A: 

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.

anon
A: 

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

tony
Ah, Todd Gardner's answer suggests this won't work (until C++0x maybe).Oh well.
tony
That's pretty much exactly what I'd have done. The only reason I stopped was when I thought that this must have been done already in boost...Until I found it wasn't...
ScaryAardvark
And then @Todd Gardner kindly explained why :)
ScaryAardvark
Yeah, as I was typing, I was wondering why I've never seen friend used like that in a template...
tony
Just FYI, a little indirection normally gets around it:template <typename T> struct Identity { typedef T type; }template <typename T> class Final { friend class Identity<T>::type; Final() {} }
Todd Gardner
@Todd Gardner: I have read and reread the standard, but it is not easy in that part. The standard says that if the *elaborated type specifier resolves to a* typedef-name *or a template* type-parameter *the elaborated-type-specifier is ill formed*. I could not find a precise definition of what *typedef-name* is, it cannot surely be *any* typedef, as that would block most metaprogramming techniques... Anyway I tested the code and g++ does allow it, while comeau compiler complains *error: typedef "type" may not be used in an elaborated type specifier*. It might be non-standard.
David Rodríguez - dribeas
Just tested with intel's compiler and it won't allow it either, I don't have a VS at hand for testing purposes
David Rodríguez - dribeas
+1  A: 

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

David Rodríguez - dribeas
+1  A: 

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; 
}
Martin York