I wounder why the classes std::future
and std::promise
are not marked with the final
specifier. The destructor is not virtual so why was final
not added? What was (is) the rationale?
-
9no class in the standard library is marked final. – bolov Apr 17 '19 at 10:49
-
3https://stackoverflow.com/questions/9298450/are-c11-standard-containers-final – bolov Apr 17 '19 at 10:52
-
there are a very few classes the standard provides that you can inherit from. since you shouldn't derive from a standard class to begin with, no reason to add unnecessary keywords. – David Haim Apr 17 '19 at 11:12
-
7Inheriting from a class with a non-virtual destructor is not inherently bad. You just have to be careful how you use it. If you never delete a derived object through a base class pointer, then you don't *need* a virtual destructor. – Jesper Juhl Apr 17 '19 at 11:33
-
2"I can't think of a reason to do it" is not the same as "nobody could possibly want to do it". I've been surprised many times by things I thought were obvious nonsense that turned out to be useful when viewed from a different perspective. – Pete Becker Apr 17 '19 at 12:15
-
With a non virtual destructor you could still: a) do private inheritance. b) do public inheritance without adding member variables (in that case I strongly recommend a `static_assert(sizeof(base) == sizeof(derived), "Error");` c) do `class MyVect : public std::vector
{}` to avoid using typedefs. – Mirko Apr 17 '19 at 12:24 -
2@Mirko: b). Adding member is fine, as state above, it is calling destructor of derived object from base class which is UB (even if same size). c). Whereas I see cases for strong typedef for `int`/`float`, I don't see for `std::vector
` :) – Jarod42 Apr 17 '19 at 13:43 -
@Jarod42 on the b case I think you might be right and might be UB (probably works all the time, but the standard does not give you any guarantee). On the c one such an advantage is to do simpler PIMPL. You can't easily forward declare a typedef to a template inside a namespace. The fact that there's a header just for forwarding `std::string` is a strong indicator that there's room for improvement there. – Mirko Apr 17 '19 at 14:12
-
1This isn't Java. "Make everything `virtual`" may be popular in some teams, but it's certainly nothing the library is ever going to mandate, which is effectively what you're suggesting it does by making things `final` when they don't have a virtual dtor. – Lightness Races in Orbit Apr 17 '19 at 15:11
3 Answers
Have a look at this contrived (admittedly nonsensical) example with std::vector
:
template <class T>
struct Example : private std::vector<T> {
void doStuff(const T& t) { this->push_back(t); }
T retrieveStuff() { return this->operator[](0); }
};
Example<int> e;
e.doStuff(42);
std::cout << e.retrieveStuff() << "\n";
This works, you can't get into UB due to std::vector::~vector
not being virtual
because you can't delete an object through a base class pointer (public
inheritance is needed there).
The inheritance here is just an implementation detail. Not recommended practice, but people probably did and do this. Once the decision is made to not break existing code by making std::vector
or other container types final
, it makes sense to stick to that with different vocabulary types like std::promise
or std::future
.

- 37,368
- 3
- 66
- 117
-
1It is cleaner to do inheritance if you're exposing base functions via `using`. That could help you to avoid reimplementing an `operator[]` that only refers to `member[]` for every possible case. – Mirko Apr 17 '19 at 12:21
-
Also, you could extend functionality as in your example using public inheritance, given that you don't add any members that change the size of the class. – Mirko Apr 17 '19 at 12:27
-
3@Mirko The example is intended to only show some non-ill-formed, non-UB use case related to the OP's question. And `public` inheritance opens the door for UB, as it allows invoking the non-`virtual` destructor.. – lubgr Apr 17 '19 at 12:28
-
Yes, but there's no problem invoking base class non virtual destructor as long as your destructor is empty and you're not extending the size of the class. – Mirko Apr 17 '19 at 12:32
-
No. I mean by size that you can't add member variables, you can always do `static_assert(sizeof(Example
) == sizeof(std::vector – Mirko Apr 17 '19 at 12:38), "");` If you extend your `Example` class with a member, it's destructor won't get called, big no-no. But if you don't, and you don't expect any special behaviuor in your destructor (it's empty), there should be no problem. -
7@Mirko "there's no problem invoking base class non virtual destructor" really? Or is it that it *appears fine* all the times you tried? – Caleth Apr 17 '19 at 14:41
-
2@Mirko You can add semantics to a derived class that require calling the proper destructor, without increasing the size of the class, so your `static_assert` is not necessarily sufficient. And in any case, the point of the answer was not to show the _best_ way to inherit from _something_, just a _legal_ way to derive from `std::vector`. – Max Langhof Apr 17 '19 at 17:03
As per [derivation]/4:
All types specified in the C++ standard library shall be non-final types unless otherwise specified.
And std::future
or std::promise
are not excepted.
And as mentioned in a comment, this issue has been discussed before. Do library implementers have the freedom to add final to non-polymorphic components?.
The resolution of this issue was that it was not considered a defect with the conclusion:
Unless the library uses the keyword
final
in a specification, the user clearly has freedom to derive from such a class, and so equally clearly, the library vendor does not have freedom to add afinal
overrider or class attribute.

- 26,289
- 6
- 39
- 76
Absence of any virtual function in a class doesn't make it non-qualifiable as the Base class. In my opinion, adding virtual
function(s) to the base class is kind of special case of making the base class as polymorphic. Many programmers careless put virtual
to functions, and especially to the destructor of the class (and comment that "Virtual destructor is necessary").
ATL, for example, relies heavily on inheritance but doesn't have any virtual functions. The (base) classes are non-polymorphic. Most (if not all) of the C++/STL classes are non-polymorphic.
One may break the rule of "Prefer containment/composition over inheritance" and derive from a class in an illogical form (One example given by lubgr); but this is doable and is valid. Sometimes it is more appropriate to inherit from a non-polymorphic class, rather than containing a class.
Template/Template-meta classes rely on inheritance where no virtual functions are involved. Attribute-inheritance is one example, where a class would inherit from various different classes (multi-inheritance), and inherit the attributes.
One very simple example would be to make a class non_copyable
, put copy-constructor/assignment-operator, etc as private/protected; and let other classes inherit from it. This way the "derived" class would inherit the non-copyable "capability/attribute" of the base class.

- 18,086
- 12
- 59
- 105