Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using declaration as overrider

We have the following simple (and slightly modified to add main and output) example in the Standard:

struct A {
    virtual void f()
    {
        cout << "A\n";
    }
};

struct B : virtual A {
    virtual void f()
    {
        cout << "B\n";
    }
};

struct C : B, virtual A {
    using A::f;
};

int main()
{
    C c;
    c.f();              // calls B​::​f, the final overrider
    c.C::f();
    return 0;
}

From which we can make a conclusion that using A::f does not present an overrider. But what wording in the Standard dictates it? Here is the wording for the final overrider from the C++17 draft ([class.virtual]p2):

<...> A virtual member function C::vf of a class object S is a final overrider unless the most derived class (4.5) of which S is a base class subobject (if any) declares or inherits another member function that overrides vf. In a derived class, if a virtual member function of a base class subobject has more than one final overrider the program is ill-formed.

And I wasn't able to find what "overrides" actually mean. If it is not defined and we consider any declaration as an overrider then we should consider the using declaration to be an overrider since [namespace.udecl]p2 says:

Every using-declaration is a declaration and a member-declaration and can therefore be used in a class definition.

I understand the intent of the Standard for using declaration to not introduce an overrider, but can someone point me to the actual quotes which say that in Standardese? That is the first part, now to the second one


Consider the following code:

#include <iostream>
#include <string>

using std::cout;

class A {
public:
    virtual void print() const {
        cout << "from A" << std::endl;
    }
};

class B: public A {
public:
    void print() const override {
        cout << "from B" << std::endl;
    }
};

class C: public A {
public:
    void print() const override {
        cout << "from C" << std::endl;
    }
};

class D: public B, public C {
public:
    using C::print;
};

int main()
{
    D d{};
    d.print();
    return 0;
}

If the using declaration doesn't introduce an overrider then we have 2 final overriders in D, hence—an undefined behavior because of

In a derived class, if a virtual member function of a base class subobject has more than one final overrider the program is ill-formed.

Right?

like image 665
ixSci Avatar asked Oct 29 '18 08:10

ixSci


1 Answers

A using declaration, while indeed a declaration as far as declarative regions are concerned, is not a function declaration. We can see it specified grammatically:

[dcl.dcl]

1 Declarations generally specify how names are to be interpreted. Declarations have the form

declaration:
  block-declaration
  nodeclspec-function-declaration
  function-definition
  template-declaration
  deduction-guide
  explicit-instantiation
  explicit-specialization
  linkage-specification
  namespace-definition
  empty-declaration
  attribute-declaration

block-declaration:
  simple-declaration
  asm-definition
  namespace-alias-definition
  using-declaration
  using-directive
  static_assert-declaration
  alias-declaration
  opaque-enum-declaration

nodeclspec-function-declaration:
  attribute-specifier-seq declarator ;

And to some extent semantically. Since the following paragraphs detail how a using declaration that introduces member functions from a base class is different to member function declarations in the derived class.

[namespace.udecl]

15 When a using-declarator brings declarations from a base class into a derived class, member functions and member function templates in the derived class override and/or hide member functions and member function templates with the same name, parameter-type-list, cv-qualification, and ref-qualifier (if any) in a base class (rather than conflicting). Such hidden or overridden declarations are excluded from the set of declarations introduced by the using-declarator.

16 For the purpose of overload resolution, the functions that are introduced by a using-declaration into a derived class are treated as though they were members of the derived class. In particular, the implicit this parameter shall be treated as if it were a pointer to the derived class rather than to the base class. This has no effect on the type of the function, and in all other respects the function remains a member of the base class.

With that in mind, if one takes into account the beginning of the first paragraph you quote:

[class.virtual]

2 If a virtual member function vf is declared in a class Base and in a class Derived, derived directly or indirectly from Base, a member function vf with the same name, parameter-type-list, cv-qualification, and ref-qualifier (or absence of same) as Base​::​vf is declared, then Derived​::​vf is also virtual (whether or not it is so declared) and it overrides Base​::​vf. For convenience we say that any virtual function overrides itself.

We can see that it is a virtual function declaration that can introduce an overrider to a virtual function in a base class. And since a using declaration is not a function declaration, it does not qualify.

The current wording is in part from CWG Defect 608. It is aimed to clarify the problematic interpretation in that report, and to decouple using declarations from the notion of virtual function overriders.


As for your second question, what's important to note in that quote is "of a base class subobject". Your code sample has two A sub-objects in D (the inheritance in that example isn't virtual). And each one has its own final overrider in B and C respectively. So the program is not ill-formed, with or without another overrider being declared in D.

The paragraph you are concerned with applies to the case of virtual inheritance. If B and C had a virtual A base, and D inherited from both without overriding print, the program would be ill-formed. And a using declaration like using C::print will not make it well-formed, again due to the reasons stated above.

like image 74
StoryTeller - Unslander Monica Avatar answered Oct 14 '22 10:10

StoryTeller - Unslander Monica