Унаследованные конструкторы, конструктор по умолчанию и видимость
Как сказано в [namespace.udecl] / 18:
[...] Объявление-использование, которое именует конструктор, не создает синоним; вместо этого дополнительные конструкторы доступны, если они были бы доступны при использовании для создания объекта соответствующего базового класса, а доступность объявления using игнорируется. [...]
Из-за этого следующий код не компилируется:
class B { protected: B(int) { } };
class D: B { using B::B; };
int main () { D d{0}; }
Он возвращает ошибку, которая более или менее одинакова со всеми основными компиляторами:
объявлен защищенным здесь
С другой стороны, следующий код компилируется:
class B { protected: B() { } };
class D: B { using B::B; };
int main () { D d{}; }
Разве он не должен скомпилироваться вместо этого по тем же причинам, которые привели к ошибке в предыдущем примере?
Что это позволяет компилировать?
2 ответа
Во втором случае наследующий конструктор не вступает в силу. Согласно правилам удаленного неявно объявленного конструктора по умолчанию, что во втором случае класса D
не нарушает (есть хорошо сформированный B::B()
за D
); компилятор объявит конструктор по умолчанию как встроенный открытый член для D
, что делает D d{};
хорошо работать.
...
T
имеет прямую или виртуальную базу, которая имеет удаленный конструктор по умолчанию, или она неоднозначна или недоступна из этого конструктора....
В первом случае вступают в силу наследующие конструкторы:
(акцент мой)
Если разрешение перегрузки выбирает унаследованный конструктор, оно становится доступным, если оно будет доступно, когда используется для создания объекта соответствующего базового класса: доступность введенного объявления-использования игнорируется.
Если разрешение перегрузки выбирает один из унаследованных конструкторов при инициализации объекта такого производного класса, то подобъект Base, от которого был унаследован конструктор, инициализируется с использованием унаследованного конструктора, а все другие базы и члены Derived инициализируются так, как если бы по умолчанию конструктор по умолчанию (инициализаторы членов по умолчанию используются, если они предусмотрены, в противном случае происходит инициализация по умолчанию).
Тогда это терпит неудачу из-за изоляции доступа.
class B { protected: B() { } };
class D: B { using B::B; };
int main () { D d{}; }
D
в этом случае не имеет определяемого пользователем конструктора, поэтому компилятор генерирует один (открытый) для вас, который вызывает B::B
(но не из-за using
, что не имеет никакого эффекта в этом случае), этот конструктор, сгенерированный компилятором, затем вызывается main.
class B { protected: B(int) { } };
class D: B { using B::B; };
int main () { D d{0}; }
Даже если D
здесь нет определяемого пользователем конструктора, сгенерированный компилятором неявно удаляется, потому что B
имеет только конструктор, который принимает int
, D
также есть конструктор, который принимает int
(using
сделал это) но этот конструктор помечен protected
и таким образом недоступен main
,