16

Comeau, g ++ (ideone) и EDG принимают следующий код без диагностики. Visual C++ успешно компилируется, хотя и с предупреждением C4624.Почему деструктор базового класса может быть доступен только при объявлении пользовательского конструктора?

class indestructible_base 
{ 
    ~indestructible_base(); 
}; 

class T : indestructible_base 
{ 
public: 
    //T() {} 
}; 

int main(void) { new T(); } 

Разобьем конструктора и он больше не компилируется.

Возможно, это правило, что если исключение возникает внутри конструктора, подобъекты должны быть уничтожены? Кажется странным, так как тело пустое и не может вызвать исключения. Тем не менее, добавьте спецификацию исключений, чтобы удостовериться, что исключение не будет выбрано (throw() или noexcept), и это не имеет значения.

Почему конструктор, объявленный пользователем, требует доступа к деструктору базового класса, в то время как конструктор, созданный автоматически?

Этот вопрос был вдохновлен: Preventing a Destructor from Running in C++

+4

FWIW, Clang отклоняет программу в обоих случаях в режиме C++ 0x, но ведет себя как g ++ в режимах C++ 98. – justin

+0

, когда вы объявляете ~ indestructible_base() как общедоступный, компилятор не жалуется. – haberdar

+1

Это может иметь отношение к исключениям. Если есть определенный пользователем конструктор, нет никакой гарантии, что он не будет бросать. Если он выбрасывает, базовый подобъект необходимо уничтожить. Но я могу ошибаться. - Он также не будет компилироваться даже без определяемого пользователем конструктора, если у вас нетривиальный член, например std :: string. (Опять же, это может вызвать конструктор, сгенерированный компилятором.) – visitor

ответ

0

Ну, если автоматически сгенерированный конструктор вызывает, возможно, метание конструктора, то it will give the same access error.

#include <string> 

class indestructible_base 
{ 
    ~indestructible_base(); 
    std::string s; // <------ this may throw 
}; 

class T : indestructible_base 
{ 
public: 
    //T() {} 
}; 

int main(void) { new T(); } 

Таким образом, я думаю, что исключения - это ответ. В ANSI ISO IEC 14882 единственным конструктором строк noexcept(true) является конструктор перемещения. Я считаю, thisдолжен компилировать, но идеон говорит нет.

+0

Но если разрешен недопустимый конструктор без бросания, не должен ли разрешаться явный конструктор 'throw()' или 'noexcept'? –

+0

Я ожидаю, что [this] (http://ideone.com/T4rnz) должен скомпилировать (но не ссылку) в порядке, хотя идее не нравится «noexcept», и у меня нет компилятора C++ 11 , – spraff

+0

FWIW, Microsoft DevStudio v10 и Microsoft DevStudio v11 dev preview могут скомпилировать этот код с или без определения T(). – Vorlauf

1

Я подозреваю, что это может быть поведение, специфичное для компилятора. Вот моя теория:

Потому что (в данном конкретном случае) неявно заданных T() является тривиальным конструктор (как определено в 12,1 (5) стандарта), компилятор даже не пытается генерировать тело для T(). Поскольку нет тела ctor, исключений, которые могли бы быть созданы во время «строительства» (чего не было на самом деле), нет необходимости генерировать dtor-вызов, поэтому нет необходимости генерировать dtor-тело , только чтобы узнать, что dtor базового класса является приватным.

Но как только T() становится нетривиальным (даже если он остается неявным), тело ctor должно быть сгенерировано, и вы получите ошибку. Что-то простое, как добавление члена в класс T, который имеет определяемый пользователем конструктор, сделает неявно определенную T() ненулевой.

Отдельная, но связанная с этим проблема заключается в том, что new T() не генерирует вызов dtor (поскольку у вас нет соответствующего delete в любом месте). В отличие от этого, если я просто заменить new T() с T dummy в вашем коде, то я получаю следующее из gcc, предполагая, что он сейчас делает полную проверку на dtor доступности (как следствие того, чтобы генерировать dtor вызов):

test.cpp: In destructor 'T::~T()': 
test.cpp:3: error: 'indestructible_base::~indestructible_base()' is private 
test.cpp:7: error: within this context 
test.cpp: In function 'int main()': 
test.cpp:12: note: synthesized method 'T::~T()' first required here 
test.cpp:12: warning: unused variable 'dummy' 
+0

Справа. Но конструктор, определяемый пользователем noexcept, также не может выбрасывать, так почему же это не удается? –

+0

@BenVoigt. Кажется, джйлин прав. Посмотрите еще раз на 12.1.5. Кажется, что не имеет ничего общего с исключениями, но вместо этого, если конструктор «тривиальный» или нет. Для тривиального конструктора все члены и базы должны иметь тривиальные конструкторы (+ другие требования). (indestructible_base также может быть членом.) Никакого деструктора не генерируется в случае тривиального конструктора. Кроме того, объявление конструктора делает его нетривиальным (за исключением случаев использования = default в C++ 11). Код хорош, потому что никто не назвал этот (не существующий) деструктор. Также обратите внимание на комментарий jjlin о delete; –

+0

Просматривая источник gcc, у меня создается впечатление, что бросает или не бросает стор. Конечно, источник довольно сложный, поэтому я не могу сказать точно, но может быть просто, что авторы компилятора просто берут консервативный подход и генерируют dtor, если им нужно генерировать ctor. – jjlin