Jens Maurer, Mark Hall, Ville Voutilainen
2010-11-12
N3206=10-0196

N3206: Override control: Eliminating Attributes

The Core Working Group discussed the syntax for override control as related to FCD National Body comments US 44 and CA 3 on 2010-11-10, 2010-11-11, and 2010-11-12. The author believes the consensus established at that time via a chain of straw polls was:

The wording below also address core issue 1065 "[[hiding]] with [[override]]".

Drafting notes: There is an ambiguity when parsing supposedly unnamed classes: "struct final { ... } * p" might define an unnamed final class, or it might define a class called "final". When resolving any ambiguity towards "identifier", unnamed classes can't ever be "final".

Add a new paragraph after 2.11 lex.name paragraph 1:

Some identifiers have a special meaning when appearing in a certain context (see table X).
Identifiers with special meaning
overridefinal
When referred to in the grammar, these identifiers are used explicitly rather than using the identifier grammar production. Any ambiguity whether a given identifier has a special meaning or not is resolved to interpret the token as a regular identifier.
Remove section 7.6.4 dcl.attr.final:
The attribute-token final specifies derivation semantics for a class and overriding semantics for a virtual function. It shall appear at most once in each attribute-list and no attribute-argument-clause shall be present. The attribute may be applied to class definitions and to virtual member functions being declared in a class definition.

If a class B is marked final and a class D is derived from B the program is ill formed.

If a virtual member function f in some class B is marked final and in a class D derived from B a function D::f overrides B::f, the program is ill-formed.

[ Example: ... ]

Remove in 7.6.5 dcl.attr.override:
The attribute-token override asserts that a virtual member function overrides a function in a base class. It shall appear at most once in each attribute-list and no attribute-argument-clause shall be present. The attribute may be applied to virtual member functions being declared in a class definition.

If a virtual member function f is marked override and does not override (10.3 class virtual) a member function of a base class the program is ill-formed.

The attribute-token hiding asserts that a class member name hides a name in a base class. It shall appear at most once in each attribute-list and no attribute-argument-clause shall be present. The attribute may be applied to class members being declared in a class definition.

If a class member is marked hiding and its name does not hide (3.3.10 basic.scope.hiding, 10.2 class.member.lookup) a class member name in a base class the program is ill-formed.

The attribute-token base_check specifies that overriding and hiding of base members is strictly checked within a class. It shall appear at most once in each attribute-list and no attribute-argument-clause shall be present. The attribute may be applied to a class definition.

In a class definition marked base_check, if a virtual member function that is neither implicitly-declared nor a destructor overrides (10.3 class.virtual) a member function of a base class and it is not marked override, the program is ill-formed. Similarly, in such a class definition, if a class member name other than that of an implicitly-declared special member function hides (3.3.10 basic.scope.hiding, 10.2 class.member.lookup) a class member name in a base class and it is not marked hiding, the program is ill-formed. [ Note: a using-declaration makes the potentially hidden name visible, avoiding the need for the hiding attribute. -- end note ] [ Example: ... ]

Change the grammar in 9 class and the following sentence:
class-head:
       class-key attribute-specifieropt identifieropt base-clauseopt
       class-key attribute-specifieropt nested-name-specifier identifier base-clauseopt
       class-key attribute-specifieropt nested-name-specifieropt simple-template-id base-clauseopt
       class-key attribute-specifieropt class-head-name class-virt-specifier-seqopt base-clauseopt
       class-key attribute-specifieropt base-clauseopt

class-head-name:
       nested-name-specifieropt class-name

class-virt-specifier-seq:
       class-virt-specifier
       class-virt-specifier-seq class-virt-specifier

class-virt-specifier:
       final
       explicit
... A class-virt-specifier-seq shall contain at most one of each class-virt-specifier. A class-specifier where the whose class-head omits the optional identifier the class-head-name defines an unnamed class. [ Note: An unnamed class thus can't be final or explicit. ]
Add a new paragraph after 9 class paragraph 2:
If a class is marked with the class-virt-specifier final and it appears as a base-type-specifier in a base-clause (10 class.derived), the program is ill-formed.
Change 9 class paragraph 10:
If a class-head class-head-name contains a nested-name-specifier, the class-specifier shall refer to a class that was previously declared directly in the class or namespace to which the nested-name-specifier refers, or in an element of the inline namespace set (7.3.1 namespace.def) of that namespace (i.e., not merely inherited or introduced by a using-declaration), and the class-specifier shall appear in a namespace enclosing the previous declaration. In such cases, the nested-name-specifier of the class-head class-head-name of the definition shall not begin with a decltype-specifier.
Change the grammar in 9.2 class.mem:
member-declarator:
    declarator virt-specifier-seqopt pure-specifieropt
    declarator virt-specifier-seqopt brace-or-equal-initializeropt
    identifieropt attribute-specifieropt virt-specifier-seqopt : constant-expression

virt-specifier-seq:
    virt-specifier
    virt-specifier-seq virt-specifier

virt-specifier:
    override
    final
    new

pure-specifier:
      = 0
Add two new paragraphs after 9.2 class.mem paragraph 8:
A virt-specifier-seq shall contain at most one of each virt-specifier. The virt-specifiers override and final shall only appear in the declaration of a virtual member function.

If a class member is marked new and its name does not hide (3.3.10 basic.scope.hiding, 10.2 class.member.lookup) a class member name in a base class the program is ill-formed.

Add a new paragraph after 10 class.derived paragraph 8:
In a class definition marked with the class-virt-specifier explicit, if a virtual member function that is neither implicitly-declared nor a destructor overrides (10.3 class.virtual) a member function of a base class and it is not marked with the virt-specifier override, the program is ill-formed. Similarly, in such a class definition, if a class member name other than that of an implicitly-declared special member function hides (3.3.10 basic.scope.hiding, 10.2 class.member.lookup) a class member name in a base class and it is not marked new, the program is ill-formed, unless the class member is a member function that overrides the corresponding member in the base class. [ Note: a using-declaration makes the potentially hidden name visible, avoiding the need for the new marker. -- end note ] [ Example:

class B {
  virtual void some_func();
  virtual void f(int);
  virtual void h(int);
  void j(int);
  void k();
  typedef B self;
};

class D explicit : public B {
  void sone_func() override;                // error: misspelled name
  void f(int) override;                     // OK: f implicitly virtual, overrides B::f
  virtual void f(long) override;            // error: non-matching argument type
  virtual void f(int) const override;       // error: non-matching cv-qualification
  virtual int f(int) override;              // error: non-matching return type
  virtual void g(long);                     // OK: new virtual function introduced
  void h(int);                              // error: h implicitly virtual, but overriding without marker
  virtual void h(double);                   // error: hides B::h without marker
  virtual void h(char *) new;               // OK
  using B::j;
  int j(double);                            // OK: not hiding due to "using"
  void j(int);                              // OK, despite `obscuring' B::j(int)
  virtual int j(void) new;                  // error: not hiding due to "using"
  int k;                                    // error: hides B::k without marker
  int m(int) new;                           // error: no hiding despite marker
  typedef D self;                           // error: hides B::self without marker
};
-- end example ]
Add two new paragraphs after 10.3 class.virtual paragraph 3:
If a virtual function f in some class B is marked with the virt-specifier final and in a class D derived from B a function D::f overrides B::f, the program is ill-formed. [ Example:
struct B {
  virtual void f() const final;
};

struct D : B {
  void f() const;    // error: D::f attempts to override final B::f
};
-- end example ]

If a virtual function is marked with the virt-specifier override and does not override a member function of a base class, the program is ill-formed. [ Example:

struct B {
  virtual void f(int);
};

struct D : B {
  void f(long) override;     // error: wrong signature overriding B::f
  void f(int) override;      // ok
};
-- end example ]