Это тонкая проблема совокупной инициализации до C ++ 20.
До C ++ 20 * B
(и A
) - типы агрегатов :
(выделено)
никакие предоставленные пользователем, унаследованные или явные конструкторы ( разрешены явно заданные по умолчанию или удалены конструкторы ) (начиная с C ++ 17) (до C ++ 20)
Тогда
Если количество предложений инициализатора меньше количества элементов and bases (since C++17)
или список инициализаторов полностью пуст, остальные элементы and bases (since C++17)
инициализируются by their default member initializers, if provided in the class definition, and otherwise (since C++14)
пустыми списками в соответствии собычные правила инициализации списка (которые выполняют инициализацию значений для не-классов и неагрегированных классов с конструкторами по умолчанию, а также агрегатную инициализацию для агрегатов).
Таким образом, B{}
создает временный объект черезагрегатная инициализация, которая будет инициализировать базовый подобъект непосредственно с пустым списком, то есть выполнить агрегатную инициализацию для создания базового подобъекта A
.Обратите внимание, что конструктор B
обойден.Проблема в том, что в таком контексте дескриптор protected
не может быть вызван для уничтожения непосредственно сконструированного базового подобъекта типа A
.(Он не жалуется на конструктор protected
, потому что он обойден агрегатной инициализацией A
.)
Вы можете изменить его на foo(B());
, чтобы избежать агрегированной инициализации;B()
выполняет значение-инициализацию , временный объект будет инициализирован конструктором B
, тогда все в порядке.
Кстати, с C ++ 20 ваш код будет работать нормально.
нет пользовательских или наследуемых конструкторов (начиная с C ++ 20)
B
(и A
) снова не являются агрегатными типами.B{}
выполняет инициализацию списка , а затем временный объект инициализируется конструктором B
;Эффект такой же, как B()
.