«временный тип 'A' имеет защищенный деструктор", но его тип - B - PullRequest
10 голосов
/ 25 июня 2019

В следующем коде, скомпилированном с Clang 8.0.0+ и -std=c++17, создание экземпляра производного класса с использованием B{} дает ошибку error: temporary of type 'A' has protected destructor. Почему A появляется в этом сообщении, когда временный имеет тип B (и поэтому должен иметь открытый деструктор)?

https://godbolt.org/z/uOzwYa

class A {
protected:
    A() = default;
    ~A() = default;
};

class B : public A {
// can also omit these 3 lines with the same result
public:
    B() = default;
    ~B() = default;
};

void foo(const B&) {}

int main() {

    // error: temporary of type 'A' has protected destructor
    foo(B{});
    //    ^

    return 0;
}

1 Ответ

11 голосов
/ 25 июня 2019

Это тонкая проблема совокупной инициализации до C ++ 20.

До C ++ 20 * BA) - типы агрегатов :

(выделено)

никакие предоставленные пользователем, унаследованные или явные конструкторы ( разрешены явно заданные по умолчанию или удалены конструкторы ) (начиная с 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)

BA) снова не являются агрегатными типами.B{} выполняет инициализацию списка , а затем временный объект инициализируется конструктором B;Эффект такой же, как B().

...