Sealed/Final implementation.

Hi All, I've made a simple implementation of making a class non-derivable. Aside from the pros/cons of actually using this, which I'm sure could be debated for months, I thought I'd present the implementation anyway to see if there is interest in it. I thought something like this might already be in Boost, but I couldn't locate it, or any threads about the subject. Usage is as: class Foo : Sealed<Foo> {}; class Bar : Foo {}; // Error. And it should all work as intended (unless I've missed something) and not allow you to instantiate Bar. --- Implementation --- namespace Detail { template <class T, class Parent> class Sealed { friend T; friend Parent; }; } template <class T> class Sealed : public virtual Detail::Sealed<T, Sealed> { friend T; }; --- kalin

On Thu, Jan 13, 2005 at 12:49:33PM +1000, kalin wrote:
Hi All, I've made a simple implementation of making a class non-derivable.
I can't compile this with GCC3 or Comeau. GCC2 lets me compile it (due to its flakey friend implementation) but it also allows Bar to inherit from Foo! This is a simpler way of preventing inheritance: class Finaliser { protected: Finaliser(){} }; class Final : private virtual Finaliser { // rest of class as normal }; I think I first saw this in Dr. Dobbs in one of their single page tips. It doesn't prevent derivation if the derived class has no user-defined constructors (since the compiler-generated ones will access the virtual base): class NotSoFinalNow : public Final { }; But does work if there are user-defined ctors: class WillNotCompile : public Final { WillNotCompile() {} }; // error There's another problem with Final that lets you derive from it in a class with user-defined constructors, if you know how. Our wiki where I work offers a chocolate bar to anyone who points it out but noone's claimed it from me yet :) jon -- "Consistency is the last refuge of the unimaginative." - Oscar Wilde

Jonathan Wakely skrev:
On Thu, Jan 13, 2005 at 12:49:33PM +1000, kalin wrote:
This is a simpler way of preventing inheritance:
class Finaliser { protected: Finaliser(){} };
class Final : private virtual Finaliser { // rest of class as normal };
[...] There's another problem with Final that lets you derive from it in a class with user-defined constructors, if you know how. Our wiki where I work offers a chocolate bar to anyone who points it out but noone's claimed it from me yet :)
class WillCompile : public Final, virtual Finaliser { public: WillCompile() {} }; Will you mail the chocolate bar to me then? Daniel

On Thu, Jan 13, 2005 at 01:45:59PM +0100, Daniel Wessl?n wrote:
Jonathan Wakely skrev:
On Thu, Jan 13, 2005 at 12:49:33PM +1000, kalin wrote:
This is a simpler way of preventing inheritance:
class Finaliser { protected: Finaliser(){} };
class Final : private virtual Finaliser { // rest of class as normal };
[...] There's another problem with Final that lets you derive from it in a class with user-defined constructors, if you know how. Our wiki where I work offers a chocolate bar to anyone who points it out but noone's claimed it from me yet :)
class WillCompile : public Final, virtual Finaliser { public: WillCompile() {} };
Exactly. I was fairly surprised to see Dr Dobbs publishing it without that caveat. I guess it works to tell users they _shouldn't_ derive from it, but as often happens you can get around it if you really want to, and you can't always stop people who deliberately try to breakthe rules.
Will you mail the chocolate bar to me then?
I didn't expect it to be a challenge for anyone reading this list but if you want some british chocolate let me know offlist :-) jon -- "Do what thou wilt shall be the whole of the law" - The Doctrines of Thelema

On Thu, Jan 13, 2005 at 01:16:26PM +0000, Jonathan Wakely wrote:
Exactly. I was fairly surprised to see Dr Dobbs publishing it without that caveat.
Just checked our wiki and apparently this was in the May 2001 edition of C/C++ Users Journal, not Dr Dobbs. Oops. jon -- The two most abundant things in the universe are Hydrogen and stupidity. - Harlan Ellison

Jonathan Wakely wrote:
On Thu, Jan 13, 2005 at 12:49:33PM +1000, kalin wrote:
Hi All, I've made a simple implementation of making a class non-derivable.
I can't compile this with GCC3 or Comeau. GCC2 lets me compile it (due to its flakey friend implementation) but it also allows Bar to inherit from Foo!
This is a simpler way of preventing inheritance:
class Finaliser { protected: Finaliser(){} };
class Final : private virtual Finaliser { // rest of class as normal };
How about: class Finaliser { friend class Final; Finaliser(){} }; class Final : private virtual Finaliser { }; ? Tom

On Thu, Jan 13, 2005 at 03:53:30PM +0000, Tom Widmer wrote:
How about:
class Finaliser { friend class Final; Finaliser(){} };
class Final : private virtual Finaliser { };
?
Yeah, that works, but Finaliser has to know about Final in advance, you can't just (virtually) derive an existing class from Final to make it final (which is what I think the OP's solution was trying to achieve with templates - but you're not allowed template params as friends). jon -- "That which needs to be proved cannot be worth much." - Nietzsche

On Thu, Jan 13, 2005 at 12:49:33PM +1000, kalin wrote:
Hi All, I've made a simple implementation of making a class non-derivable.
I can't compile this with GCC3 or Comeau. GCC2 lets me compile it (due to its flakey friend implementation) but it also allows Bar to inherit from Foo!
Should have mentioned I only tested it on VS71. Did you attempt to instantiate the Bar, or just inherit? The earlier solution would not even warn about inheritance, but disallowed instantiation.. not an optimal solution, hopefully something better can be figured out. :)
This is a simpler way of preventing inheritance:
class Finaliser { protected: Finaliser(){} };
class Final : private virtual Finaliser { // rest of class as normal };
I think I first saw this in Dr. Dobbs in one of their single page tips.
I saw a similar thing in Design and Evolution of C++, but I didn't like having to worry about derivation type. The goal was to remove any need to specify correct access or virtual-ness of the base class, and make it less prone to mistakes.
It doesn't prevent derivation if the derived class has no user-defined constructors (since the compiler-generated ones will access the virtual base): class NotSoFinalNow : public Final { };
But does work if there are user-defined ctors: class WillNotCompile : public Final { WillNotCompile() {} }; // error
I'll do some more tests with regard to existing/nonexisting ctors/dtors and see if something a bit more robust can be created. :) Thanks for the feedback. kalin
participants (5)
-
Daniel Wesslén
-
Gennadiy Rozental
-
Jonathan Wakely
-
kalin
-
Tom Widmer