Как заявлено [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
имеет прямую или виртуальную базу, которая имеет удаленный конструктор по умолчанию, или она неоднозначна или недоступна из этого конструктора.…
Для 1-го случая наследующие конструкторы вступает в силу:
(акцент мой)
Если разрешение перегрузки выбирает унаследованный конструктор, это
доступно, если оно будет доступно при использовании для создания объекта
соответствующего базового класса: доступность
использование-объявление, которое ввело это, игнорируется.Если разрешение перегрузки выбирает один из унаследованных конструкторов, когда
инициализация объекта такого производного класса, затем подобъект 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
,