Рассмотрим следующий код:
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-выражение) в области видимости. […] Для 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 base::base
назначить? Мне кажется, что ответили [class.qual] p2
:
В поиске, в котором имена функций не игнорируются и
вложенное имя спецификатор назначает классC
:
если имя указано после вложенное имя спецификатор, когда посмотрел в
C
, является введенным именем классаC
, или жев используя декларирование это член-декларация, если имя указано после вложенное имя спецификатор такой же как
идентификатор […] в последнем компоненте вложенное имя спецификаторвместо этого имя считается именем конструктора класса
C
,
Существует сноска, поясняющая, что означает «поиск, в котором имена функций не игнорируются»:
Поиск, в котором имена функций игнорируются, включает имена, появляющиеся в вложенное имя спецификатор, уточненный тип Спецификатор, или базовый спецификатор.
Ни один из этих случаев не подходит для поиска имени, о котором идет речь, поэтому мне кажется, что этот параграф применим и говорит, что using base::base
обозначает конструктор (что также является интуитивно понятным, учитывая, что это объявление наследующего конструктора).
Найдя объявление (обозначающее конструктор базового класса) в области видимости производного класса, мы продолжаем [class.member.lookup] p4
:
Если результирующий набор объявлений не пустой, набор подобъектов содержит
C
Сам, и расчет завершен.
Таким образом, поскольку поиск имени нашел результат в области видимости производного класса, он не переходит к поиску в области базового класса (где он найдет нагнетаемого имя класса base
). [Кроме того, даже если поиск по имени продолжался в области действия базового класса, я не вижу ничего, что могло бы устранить неоднозначность между конструктором и нагнетаемого имя класса].
Где мои рассуждения идут не так?
Стандарт идет на боль, чтобы указать, что у конструктора нет имени. Его нельзя найти по имени, потому что у него нет имени.
C ++ 11 §12.1 / 1
» Конструкторы не имеют имен.
C + 11 §12.1 / 2
» Поскольку у конструкторов нет имен, они
никогда не найден во время поиска имени.
Других решений пока нет …