This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 116a. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.

2024-12-19


2555. Ineffective redeclaration prevention for using-declarators

Section: 9.9  [namespace.udecl]     Status: drafting     Submitter: Christof Meerwald     Date: 2022-03-23

Consider:

  template<int I>
  struct C { };

  struct B
  {
    C<1> foo();
    C<1> bar();
  };

  struct D : B
  {
    using B::foo;
    C<2> foo(this B &);

    using B::bar;
    C<2> bar(this D &);
  };

  struct DD : D
  {
    using D::foo;
    using D::bar;
  };

  void bar(D d, DD dd)
  {
    d.foo();
    dd.foo();

    d.bar();
    dd.bar();
  }

Which functions are called?

Subclause 9.9 [namespace.udecl] paragraph 11 specifies:

The set of declarations named by a using-declarator that inhabits a class C does not include member functions and member function templates of a base class that correspond to (and thus would conflict with) a declaration of a function or function template in C.

The definition of "corresponds" considers the type of the implicit object parameter, which is a deviation from the status quo ante for a simple example like this one:

  struct B {
    void f();    // #1
  };
  struct D : B {
    void f();
    using B::f;  // should not name #1
  };

Suggested resolution:

Change in 9.9 [namespace.udecl] paragraph 11 as follows:

The set of declarations named by a using-declarator that inhabits a class C does not include member functions and member function templates of a base class that, when considered as members of C, correspond to (and thus would conflict with) a declaration of a function or function template in C.

[ Example:

  struct B {
    virtual void f(int);
    virtual void f(char);
    void g(int);
    void h(int);
    void i();
    void j();
  };

  struct D : B {
    using B::f;
    void f(int);   // OK, D::f(int) overrides B::f(int)
  
    using B::g;
    void g(char);  // OK
  
    using B::h;
    void h(int);   // OK, D::h(int) hides B::h(int)

    using B::i;
    void i(this B &);  // OK

    using B::j;
    void j(this D &);  // OK, D::j() hides B::j()
  };

  void k(D* p)
  {
    p->f(1);        // calls D::f(int)
    p->f('a');      // calls B::f(char)
    p->g(1);        // calls B::g(int)
    p->g('a');      // calls D::g(char)
    p->i();         // calls B::i, because B::i as a member of D is a better match than D::i
    p->j();         // calls D::j
  }
  ...