Final classes in C++ -- what for?

601 views
Skip to first unread message

Andrzej Krzemieński

unread,
Oct 21, 2012, 4:42:51 PM10/21/12
to std-dis...@isocpp.org
Hi,
I recently participated in a discussion on final classes in C++11 (see http://akrzemi1.wordpress.com/2012/09/30/why-make-your-classes-final/). The participants (myself included) were under the impression that while final classes make sense in some languages heavily biased towards OO, like Java, there appears to be no convincing motivation for adding final classes to C++.

Does anyone in this group know what was the motivation for adding final classes (classes that one cannot inherit from) to C++11?

Regards,
&rzej

Ville Voutilainen

unread,
Oct 21, 2012, 4:46:06 PM10/21/12
to std-dis...@isocpp.org
Well, people try and write final classes for a variety of (imho
dubious) reasons, and making
final-on-class mean "can't override + can't derive from" was made in order
to be similar to other languages. I have lately been toying with the
idea of having an
annotation that prevents further overriding but allows derivation.

Peter Sommerlad

unread,
Oct 22, 2012, 10:28:53 AM10/22/12
to std-dis...@isocpp.org
On 21.10.2012, at 22:42, Andrzej Krzemieński wrote:

> no convincing motivation for adding final classes to C++.

I agree with you and I believe (don't know) that mainly the proponents wanted to have similarity with Java/C# and others without a really compelling use case (at least not compelling for me).

With inheriting ctors one might even make use of empty subclasses to create a functional identical type, but in a different namespace, for example, to alleviate the problem of ADL in templates.
For example as follows:

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
namespace X{
using std::vector;
struct vec:vector<int>{ // vec is a new type not an alias
using vector<int>::vector; // inherit ctors
};
using std::ostream;
using out=std::ostream_iterator<int>;
ostream& operator<<(ostream &os, vec const &v){
copy(v.begin(),v.end(),out{os,","});
return os;
}
}
void works_with_inheriting_ctors(){
std::vector<X::vec> vv{{1,2,3},{4,5,6}};
using outv=std::ostream_iterator<X::vec>;
copy(vv.begin(),vv.end(),outv{std::cout,"\n"});
}

--
Prof. Peter Sommerlad

Institut für Software: Bessere Software - Einfach, Schneller!
HSR Hochschule für Technik Rapperswil
Oberseestr 10, Postfach 1475, CH-8640 Rapperswil

http://ifs.hsr.ch http://cute-test.com http://linticator.com http://includator.com
tel:+41 55 222 49 84 == mobile:+41 79 432 23 32
fax:+41 55 222 46 29 == mailto:peter.s...@hsr.ch





Marc

unread,
Oct 22, 2012, 4:56:15 PM10/22/12
to std-dis...@isocpp.org
On Sunday, October 21, 2012 10:42:51 PM UTC+2, Andrzej Krzemieński wrote:
Does anyone in this group know what was the motivation for adding final classes (classes that one cannot inherit from) to C++11?

Probably not  the motivation for creating the feature, but final classes help the compiler devirtualize function calls.

Ville Voutilainen

unread,
Oct 22, 2012, 4:58:25 PM10/22/12
to std-dis...@isocpp.org
On 22 October 2012 23:56, Marc <marc....@gmail.com> wrote:
> Probably not the motivation for creating the feature, but final classes
> help the compiler devirtualize function calls.

We wouldn't need final classes for that, if we'd have final member functions.
Final member functions have that use, and a handful of others, but a really
good use case for a final class is still something that many people
are looking for.

Dean Michael Berris

unread,
Oct 22, 2012, 5:02:02 PM10/22/12
to std-dis...@isocpp.org
One of the idioms I remember working with some moons ago was the
concept of a class that wasn't meant to be derived from. This is an
important design tool for conveying exactly that a class was designed
to not be polymorphic. In an OOP hierarchy, a derived class that was
meant to be a leaf should be marked as such to avoid unintended
consequences.

I can cook up a contrived example but I'll avoid that for now.

Cheers

--
Dean Michael Berris | Software Engineer
Google

Nevin Liber

unread,
Oct 22, 2012, 5:11:34 PM10/22/12
to std-dis...@isocpp.org
On 22 October 2012 16:02, Dean Michael Berris <dbe...@google.com> wrote:
One of the idioms I remember working with some moons ago was the
concept of a class that wasn't meant to be derived from. This is an
important design tool for conveying exactly that a class was designed
to not be polymorphic.

But polymorphism isn't the only reason for derivation in C++, and final pretty much breaks all other uses.  See the discussion around c++std-lib-31619 for more details.
--
 Nevin ":-)" Liber  <mailto:ne...@eviloverlord.com(847) 691-1404

Dean Michael Berris

unread,
Oct 22, 2012, 5:18:37 PM10/22/12
to std-dis...@isocpp.org
On Tue, Oct 23, 2012 at 8:11 AM, Nevin Liber <ne...@eviloverlord.com> wrote:
> On 22 October 2012 16:02, Dean Michael Berris <dbe...@google.com> wrote:
>>
>> One of the idioms I remember working with some moons ago was the
>> concept of a class that wasn't meant to be derived from. This is an
>> important design tool for conveying exactly that a class was designed
>> to not be polymorphic.
>
>
> But polymorphism isn't the only reason for derivation in C++, and final
> pretty much breaks all other uses. See the discussion around
> c++std-lib-31619 for more details.

I am painfully aware of that. Unfortunately there are not really any
other "simple" ways to annotate your class in a standard manner to say
that it cannot be derived from. For people who want this facility,
it's a very important one -- preventing runtime issues by making bad
usage a compile-time error. I personally don't use it that often, but
I've found myself wanting this a while back. That was solved by
documentation and hoping nobody else did the wrong thing, but it could
have very have been solved easily by marking a class final and being
done with it.

Klaim - Joël Lamotte

unread,
Oct 23, 2012, 9:05:48 AM10/23/12
to std-dis...@isocpp.org
Hi,

To me the main problem is that C++ inheritance tends to hide potential traps
that are hard to identify when the class isn't designed to be inheritable at some point, 
which makes any solution to explicitly mark a class as not designed for inheritance 
looks like an excellent solution.

I don't have any argument for or against final classes, but I do have some 
class I marked final. My thinking was the following: 

  I'm designing a class, which name and purpose gives the false assumption
  that both inheritance and composition might be good usage of it, but it's
  designed to be used for composition only. Therefore, any way better
  than comments to document this is good. Therefore, using final class 
   seems a good idea.

Most of my classes are either designed for inheritance or not but it's 
explicit by name/purpose of the class. Or inheritance isn't a problem because
of the implementation being accidentally ok for it.
It's only when I had a big doubt about the potential misuse that I used final.

But I'm not experienced enough with it yet to say It's useful or not, and I don't have
any other developer looking at my code for the moment, so I'll certainly see the point
(or not) once I'm in a team.

My 2 cents.

Joel Lamotte

Ville Voutilainen

unread,
Oct 23, 2012, 9:24:51 AM10/23/12
to std-dis...@isocpp.org
On 23 October 2012 16:05, Klaim - Joël Lamotte <mjk...@gmail.com> wrote:
> I'm designing a class, which name and purpose gives the false assumption
> that both inheritance and composition might be good usage of it, but it's
> designed to be used for composition only. Therefore, any way better
> than comments to document this is good. Therefore, using final class
> seems a good idea.

Do remember that private inheritance is a form of composition. Also remember
that having a construction veneer that inherits from a non-polymorphic base
to add new constructors and other such state is another valid use case. For
those reasons, I find it dubious to prevent inheritance completely.

I would like to have facility that makes the virtual functions in a class final,
but allows inheritance. You can't achieve that by just overriding every
virtual and marking them final, because you might want to do that for
a dependent base of a class template. I plan to do an experimental
implementation of a final_overrider keyword and potentially create
a proposal for it. Example:

struct AbstractBase { virtual void f() = 0;};

struct ConcreteBase : AbstractBase {void f();};

template <class T> struct X FinalOverrider : T
{
};

final_overrider<AbstractBase> X1; // ill-formed, can't have final
overriders if there are non-overridden pure virtuals left

final_overrider<ConcreteBase> X2; // ok

struct FurtherOverrider : FinalOverrider<ConcreteBase> // inheritance ok
{
void f(); // ill-formed, FinalOverrider is the final overrider,
and f() is final from that point onwards
};

and then, also, with multiple inheritance

struct AnotherConcreteBase {virtual void g();};

struct FurtherOverrider2 : AnotherConcreteBase, FinalOverrider<ConcreteBase>
{
void g(); // ok, we can still override functions from the base
that allows it
void f(); // ill-formed, the FinalOverrider 'branch' of the bases
is the final overrider
};

Ville Voutilainen

unread,
Oct 23, 2012, 9:26:08 AM10/23/12
to std-dis...@isocpp.org
On 23 October 2012 16:24, Ville Voutilainen <ville.vo...@gmail.com> wrote:
> template <class T> struct X FinalOverrider : T

Oops, that should be
template <class T> struct FinalOverrider final_overrider : T

Klaim - Joël Lamotte

unread,
Oct 23, 2012, 9:49:35 AM10/23/12
to std-dis...@isocpp.org


On Tue, Oct 23, 2012 at 3:24 PM, Ville Voutilainen <ville.vo...@gmail.com> wrote:
On 23 October 2012 16:05, Klaim - Joël Lamotte <mjk...@gmail.com> wrote:
>   I'm designing a class, which name and purpose gives the false assumption
>   that both inheritance and composition might be good usage of it, but it's
>   designed to be used for composition only. Therefore, any way better
>   than comments to document this is good. Therefore, using final class
>    seems a good idea.

Do remember that private inheritance is a form of composition. Also remember
that having a construction veneer that inherits from a non-polymorphic base
to add new constructors and other such state is another valid use case. For
those reasons, I find it dubious to prevent inheritance completely.

Private inheritance and member composition aren't exactly equivalent and one is more well understood and provide less unwanted behavior than the other.
I wouldn't use private inheritance for composition, just because it makes communication with other programmers ambiguous (if I don't HAVE to use it, obviously).
Also, it's not really a problem as if there is no private inheritance allowed for a class, you still have member composition available.

I didn't understand what you mean with the constructors, nor what's the point of the following explanation? 

The types I marked final were not child classes. They were classes named, for example Task. The problem
with such name is that it's a bit abstract (for good reasons) yet depending on the implementation it could suggest "inherit from me" or "provide a function to call when I'm executed".
In TBB it's the first one. In my class it's the second one (for some context-specific reasons). 
Using this type through inheritance will generate hard to find runtime errors, even with private inheritance.
It's easy to add comments for this, but as I said, if there is a better way to document it...

Not sure yet if it's still showing a use case, but as I said, it would need more time to experiment.

Joel Lamotte


Ville Voutilainen

unread,
Oct 23, 2012, 10:14:23 AM10/23/12
to std-dis...@isocpp.org
On 23 October 2012 16:49, Klaim - Joël Lamotte <mjk...@gmail.com> wrote:
> I didn't understand what you mean with the constructors, nor what's the
> point of the following explanation?

Construction veneers? See
http://synesis.com.au/resources/articles/cpp/veneers.pdf
You add functionality via derivation, and you can add constructors
(although that
might be called a construction shim instead of a veneer).

template <class T> struct ContainerFromList : T
{
ContainerFromList(const list<T>& src) : T(src.begin(), src.end()) {}
};

list<Foo> blah{get_list_from_wherever()};
ContainerFromList<vector<Foo>> x{blah};
ContainerFromList<deque<Foo>> x2{blah};

Ville Voutilainen

unread,
Oct 23, 2012, 10:21:31 AM10/23/12
to std-dis...@isocpp.org
On 23 October 2012 17:14, Ville Voutilainen <ville.vo...@gmail.com> wrote:
> template <class T> struct ContainerFromList : T
> {
> ContainerFromList(const list<T>& src) : T(src.begin(), src.end()) {}
> };

Uh oh, on-the-fly code strikes again, that shouldn't be list<T>, but list<U>.
Anyway, the idea is to derive from a container and provide new constructors
for it. That's a specific example of why "not polymorphic" doesn't always
mean "not to be derived from".

Klaim - Joël Lamotte

unread,
Oct 23, 2012, 10:39:14 AM10/23/12
to std-dis...@isocpp.org
On Tue, Oct 23, 2012 at 4:14 PM, Ville Voutilainen <ville.vo...@gmail.com> wrote:
On 23 October 2012 16:49, Klaim - Joël Lamotte <mjk...@gmail.com> wrote:
> I didn't understand what you mean with the constructors, nor what's the
> point of the following explanation?

Construction veneers? See
http://synesis.com.au/resources/articles/cpp/veneers.pdf

Thanks, looks like I don't know this, I'll read it ASAP.

Joel Lamotte

Chris Jefferson

unread,
Oct 23, 2012, 10:43:21 AM10/23/12
to std-dis...@isocpp.org
On 22/10/12 22:11, Nevin Liber wrote:
On 22 October 2012 16:02, Dean Michael Berris <dbe...@google.com> wrote:
One of the idioms I remember working with some moons ago was the
concept of a class that wasn't meant to be derived from. This is an
important design tool for conveying exactly that a class was designed
to not be polymorphic.

But polymorphism isn't the only reason for derivation in C++, and final pretty much breaks all other uses.  See the discussion around c++std-lib-31619 for more details.

The only other use case I am aware of is making use of the empty-base optimisation, which has always seemed like a bit of a hack to me. Providing some other way of making empty members take up no space (annotation?) would seem less hacky, and avoid the need for inheritance.

Chris

Reply all
Reply to author
Forward
0 new messages