Consider the following code:
struct base {};
struct derived : public base {
using base::base;
base foo() const; // how does name lookup on 'base' here work?
};
Intuitively, it's clear that this code is valid, and it does compile (tested with gcc and clang).
However, I'd like to understand what in the standard makes it valid. Specifically, I'd like to understand how name lookup for base
in base foo()
finds the base class type rather than the inherited constructor.
Here's my analysis of the standard wording, showing that it should resolve to the constructor. It's probably wrong, but I'd like to understand where I'm going wrong.
I started with [class.member.lookup] p1
:
Member name lookup determines the meaning of a name (id-expression) in a class scope. [...] For an id-expression, name lookup begins in the class scope of
this
p7
tells us what the result of name lookup is:
The result of name lookup for [a member name]
f
in [a class scope]C
is the declaration set of S(f, C)
I'm trying to follow this procedure with C
being derived
, and f
being the use of base
in base foo()
.
"Declaration set" is defined in p3
:
The lookup set for
f
inC
, called S(f, C), consists of two component sets: the declaration set, a set of members namedf
; [...]
p4
tells us what goes into the declaration set:
If
C
contains a declaration of the namef
, the declaration set contains every declaration off
declared inC
that satisfies the requirements of the language construct in which the lookup occurs.
using base::base
is a declaration of the name base
(f
) in derived
(C
). The paragraph goes on to give examples of what it means for a declaration not to satisfy the requirements of the language construct in which the lookup occurs, but there's nothing there that would exclude using base::base
from this lookup.
Next, further down in p3
we're told how using-declarations in the declaration set are handled:
In the declaration set, using-declarations are replaced by the set of designated members that are not hidden or overridden by members of the derived class
So what members does using base::base
designate? Seems to me like that's answered by [class.qual] p2
:
In a lookup in which function names are not ignored and the nested-name-specifier nominates a class
C
:
if the name specified after the nested-name-specifier, when looked up in
C
, is the injected-class-name ofC
, orin a using-declaration that is a member-declaration, if the name specified after the nested-name-specifier is the same as identifier [...] in the last component of the nested-name-specifier
the name is instead considered to name the constructor of class
C
.
There is a footnote which clarifies what a "lookup in which function names are not ignored" means:
Lookups in which function names are ignored include names appearing in a nested-name-specifier, an elaborated-type-specifier, or a base-specifier.
None of these is the case for the name lookup in question, so it seems to me that this paragraph applies, and says that using base::base
designates the constructor (which is also what you'd expect intuitively, given that it's the inheriting constructor declaration).
Having found a declaration (designating the base class constructor) in the derived class scope, we continue following [class.member.lookup] p4
:
If the resulting declaration set is not empty, the subobject set contains
C
itself, and calculation is complete.
That is, since name lookup found a result in the derived class scope, it does not proceed to look in the base class scope (where it would find the injected-class-name base
). [As an aside, even if name lookup continued into the base class scope, I don't see anything that would disambiguate between the constructor and the injected-class-name].
Where is my reasoning going wrong?
The standard goes to pains to point out that a constructor doesn't have a name. It can't be found by name lookup because it doesn't have a name.
C++11 §12.1/1C+11 §12.1/2” Constructors do not have names.
” Because constructors do not have names, they are never found during name lookup.