Я собираюсь создать иерархию классов исключений, которая концептуально выглядит примерно так:
#include <iostream>
#include <stdexcept>
class ExceptionBase : public std::runtime_error {
public:
ExceptionBase( const char * msg ) : std::runtime_error(msg) {}
};
class OperationFailure : virtual public ExceptionBase {
public:
using ExceptionBase::ExceptionBase;
};
class FileDoesNotExistError : virtual public ExceptionBase {
public:
using ExceptionBase::ExceptionBase;
};
class OperationFailedBecauseFileDoesNotExistError
: public OperationFailure, FileDoesNotExistError {
public:
using ExceptionBase::ExceptionBase; // does not compile
};
int main() {
OperationFailedBecauseFileDoesNotExistError e("Hello world!\n");
std::cout << e.what();
}
Все конструкторы должны выглядеть так же, как конструктор ExceptionBase
учебный класс. Производные исключения различаются только в отношении их типа, в противном случае никаких дополнительных функций нет. Последний тип исключения, упомянутый в приведенном выше коде, также должен иметь эти конструкторы. Возможно ли это с помощью функции наследования конструкторов стандарта C ++ 11? Если это невозможно: каковы альтернативы?
(Кстати: в приведенном выше коде классы OperationFailure
а также FileDoesNotExistError
не компилируется с gcc 4.8, но с clang 3.4. По-видимому, gcc отклоняет наследуемые конструкторы для виртуальных баз. Было бы интересно узнать, кто здесь. Оба компилятора отклонили класс OperationFailedBecauseFileDoesNotExistError
потому что наследующий конструктор не наследуется от прямой базы.)
Когда используя декларирование используется для наследования конструкторов, для этого требуется прямой базовый класс [namespace.udecl] / 3
Если такой используя декларирование называет конструктор вложенное имя спецификатор назовите прямой базовый класс определяемого класса; в противном случае он вводит набор объявлений, найденных при поиске имени члена.
То есть в вашем случае используя декларирование в OperationFailedBecauseFileDoesNotExistError
не наследует, но повторно объявляет (как псевдоним) или отображает имя ctor ExceptionBase
,
Вам придется написать ненаследующий ctor для OperationFailedBecauseFileDoesNotExistError
,
Между прочим, это хорошо для не виртуальных базовых классов: объявление использования для наследования ctors переписано как:
//using ExceptionBase::ExceptionBase;
OperationFailure(char const * msg)
: ExceptionBase( static_cast<const char*&&>(msg) )
{}
Поскольку вы можете только инициализировать прямой базовый класс (или виртуальный базовый класс) в мем-инициализатора-лист, имеет смысл для не-виртуальных базовых классов ограничивать используя декларирование наследовать ctors только от прямых базовых классов.
Авторы предложения о наследующих ctors знали, что это нарушает поддержку виртуальных ctors базового класса, см. N2540:
Как правило, наследование определений конструктора для классов с виртуальными базами будет некорректным, если только виртуальная база не поддерживает инициализацию по умолчанию или виртуальная база не является прямой базой и не названа в качестве переадресованной базы. Аналогично, все члены данных и другие прямые базы должны поддерживать инициализацию по умолчанию, иначе любая попытка использовать наследующий конструктор будет неверной. Примечание: плохо сформировано при использовании, не заявлено.
Наследование конструкторов похоже на введение функций-оболочек для всех указанных вами конструкторов. В вашем случае вы должны вызвать конкретные конструкторы обоих OperationFailure
а также FileDoesNotExistError
но представленные оболочки будут вызывать только одну из них.
Я только что проверил последний проект C ++ 11 (раздел 12.9), но на самом деле он явно не охватывает ваш случай.