Рассмотрим следующий код:
struct base {};
struct derived : public base {
using base::base;
base foo() const; // how does name lookup on 'base' here work?
};
Интуитивно понятно, что этот код корректен и компилируется (проверено с помощью gcc и clang).
Однако я хотел бы понять, что в стандарте делает его действительным. В частности, я хотел бы понять, как поиск имени для base
в base foo()
находит тип базового класса, а не унаследованный конструктор.
Вот мой анализ стандартной формулировки, показывающий, что она должна разрешаться конструктором. Это, вероятно, неправильно, но я хотел бы понять, где я ошибаюсь.
Я начал с [class.member.lookup] p1
:
Поиск имени члена определяет значение имени (id-expression) в области действия класса. [...] Для id-выражения поиск имени начинается в области класса
this
p7
говорит нам, каков результат поиска имени:
Результатом поиска имени для [имя члена]
f
в [области действия класса]C
является набор объявлений S(f, C)
Я пытаюсь следовать этой процедуре с C
в качестве derived
, а f
в качестве использования base
в base foo()
.
«Набор объявлений» определен в p3
:
Набор поиска для
f
вC
, называемый S(f, C), состоит из двух наборов компонентов: набор объявлений, набор участники по имениf
; [...]
p4
сообщает нам, что входит в набор объявлений:
Если
C
содержит объявление имениf
, набор объявлений содержит каждое объявлениеf
, объявленное вC
, которое удовлетворяет требованиям языковой конструкции, в которой происходит поиск.
using base::base
— это объявление имени base
(f
) в derived
(C
). Далее в абзаце приводятся примеры того, что означает объявление не для удовлетворения требований языковой конструкции, в которой происходит поиск, но там нет ничего, что исключало бы using base::base
из этого поиска.
Далее, далее в p3
нам рассказывают, как обрабатываются использующие-объявления в наборе объявлений:
В наборе объявлений using-declarations заменяется набором назначенных членов, которые не скрыты и не переопределены членами производного класса.
Итак, каких членов обозначает using base::base
? Мне кажется, что на это ответил [class.qual] p2
:
В поиске, в котором имена функций не игнорируются, а описатель вложенного имени назначает класс
C
:
если имя, указанное после описателя вложенного имени, при поиске в
C
является введенным именем классаC
илив объявлении-использования, которое является объявлением-членом, если имя, указанное после описателя вложенного-имени, совпадает с идентификатор [...] в последнем компоненте описателя вложенного имени
вместо этого имя считается именем конструктора класса
C
.
Существует сноска, разъясняющая, что означает «поиск, в котором имена функций не игнорируются»:
Поиски, в которых игнорируются имена функций, включают имена, появляющиеся в вложенном-спецификатор-имени, спецификатор-уточненного-типа или базовый-спецификатор.
Ни один из них не относится к рассматриваемому поиску имени, поэтому мне кажется, что этот абзац применим и говорит, что using base::base
обозначает конструктор (что также интуитивно понятно, учитывая, что это объявление наследующего конструктора).
Найдя объявление (обозначающее конструктор базового класса) в области действия производного класса, мы продолжаем следовать [class.member.lookup] p4
:
Если результирующий набор объявлений не пуст, набор подобъектов содержит сам
C
, и расчет завершен.
То есть, поскольку поиск имени нашел результат в области действия производного класса, он не переходит к поиску в области действия базового класса (где было бы найдено внедренное имя класса base
). [Кроме того, даже если поиск имени продолжится в области действия базового класса, я не вижу ничего, что устраняло бы неоднозначность между конструктором и внедренным именем класса].
Где мои рассуждения идут не так?