Both the override specifier and final specifier were added in C++11. They differ from other specifiers added to C++11 such as constexpr and decltype, in that they are not keywords and so are available for use as identifiers:
int main()
{
int override = 0 ; // Ok
int final = 0 ; // Ok
//int constexpr = 0 ; // Error
}
They are referred to as identifiers with special meaning, which is covered in the draft C++11 standard section 2.11
[lex.name] (emphasis mine):
The identifiers in Table 3 have a special meaning when appearing
in a certain context. When referred to in the grammar, these
identifiers are used explicitly rather than using the identifier
grammar production. any ambiguity as to whether a given identifier has
a special meaning is resolved to interpret the token as a regular
identifier.
and Table 3 -- Identifiers with special meaning lists both override and final.
Why did these two specifiers end up being identifiers with special meaning instead of keywords?
Adding new keywords is difficult because it takes away identifiers from the user. It ends up being a trade-off between choosing identifiers that potentially break old code currently using the identifier or choosing names that are highly unlikely to break old code but are ugly or not meaningful to the way they are being used.
In this specific case override and final end up being used in the grammar in places where no user identifiers can appear. So in those places the identifiers can have a special meaning and outside of those contexts they can be treated as a regular identifier leaving the identifiers available for users. C++/CLI
has used this technique since 2005
and they are called context-sensitive keywords which is covered in the C++/CLI standard section 9.1.1
Identifiers.
We can see in the write-up that addresses the trade-offs of the different methods of adding support for virtual control attributes in N3163: Override Control Using Contextual Keywords. It discussed three options:
Using [[attributes]]
, which was deemed undesirable for reasons including they are just keywords in disguise (modified example from paper below):
class A : public B {
virtual void f [[override]] () { ... }
virtual void h [[final]] () { ... }
};
Use reserved keywords, which potentially breaks existing code unless ugly names are chosen (modified example from paper below):
class A : public B {
virtual void f override_func () { ... }
virtual void h final_func () { ... }
};
Use context-sensitive keywords, which does not break existing code allows for nice names (modified example from paper below):
class A : public B {
virtual void f() override { ... }
virtual void h() final { ... }
};
The following paragraph from the paper sums up the argument for using context sensitive keywords over the other two choices (emphasis mine):
The observation was made in Rapperswil that this approach can make
error recovery and syntax highlighting more difficult. For example,
syntax highlighting is a bit harder because instead of globally
highlighting the keyword you need to do parsing to know whether the
identifier is in the location where it has special meaning and should
be highlighted. But this is not exceptionally difficult, particularly
not in comparison to other far more difficult things we already have
to do in C++ compared to other languages .
These are minor inconveniences for a few compiler writers for a week,
but are seamless for users. This is the right tradeoff. Otherwise,
having ugly globally reserved names (Option 2) or inappropriate and
visibly bolted
- on attributes (Option 1) make things easier for a few compiler writers for a week, but are things millions of users will have to live
with forever.
The changes were applied to the standard via N3206: Override control: Eliminating Attributes and N3272: Follow-up on override control.