Наследование конструкторов и виртуальных базовых классов

Я собираюсь создать иерархию классов исключений, которая концептуально выглядит примерно так:

#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потому что наследующий конструктор не наследуется от прямой базы.)

6

Решение

Когда используя декларирование используется для наследования конструкторов, для этого требуется прямой базовый класс [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:

Как правило, наследование определений конструктора для классов с виртуальными базами будет некорректным, если только виртуальная база не поддерживает инициализацию по умолчанию или виртуальная база не является прямой базой и не названа в качестве переадресованной базы. Аналогично, все члены данных и другие прямые базы должны поддерживать инициализацию по умолчанию, иначе любая попытка использовать наследующий конструктор будет неверной. Примечание: плохо сформировано при использовании, не заявлено.

2

Другие решения

Наследование конструкторов похоже на введение функций-оболочек для всех указанных вами конструкторов. В вашем случае вы должны вызвать конкретные конструкторы обоих OperationFailure а также FileDoesNotExistError но представленные оболочки будут вызывать только одну из них.


Я только что проверил последний проект C ++ 11 (раздел 12.9), но на самом деле он явно не охватывает ваш случай.

1

По вопросам рекламы [email protected]