Унаследованные конструкторы, конструктор по умолчанию и видимость

Как сказано в [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,

Другие вопросы по тегам