Извлечение класса из виртуальной базы без конструктора по умолчанию

Я пишу небольшую иерархию классов исключений для разрабатываемого приложения на C ++, и у меня возникают проблемы с косвенным выводом из std::runtime_error, Вот код, аналогичный тому, что я написал до сих пор:

class RuntimeException : public virtual boost::exception, public virtual std::runtime_error {
public:
virtual ~RuntimeException() {}
RuntimeException() : runtime_error("A RuntimeException occurred.") {}
RuntimeException(const std::string& what) : runtime_error(what) {}
};

class IllegalArgumentException : public virtual RuntimeException {
public:
IllegalArgumentException() : RuntimeException("An IllegalArgumentException occurred.") {}
IllegalArgumentException(const std::string& what) : RuntimeException(what) {}
};

RuntimeException класс компилируется без проблем, но IllegalArgumentException отказывается от компиляции на VS2015, генерируя ошибку: no default constructor exists for class "std::runtime_error" для обоих конструкторов IllegalArgumentException, Это бросает вызов моему пониманию иерархий наследования C ++, так как я ожидал, что этот код хорошо скомпилируется.

Я понимаю, что IllegalArgumentException должен компилировать, потому что, хотя это правда, что std::runtime_error не имеет конструктора по умолчанию, его конструктор вызывается конструктором для RuntimeException, Но очевидно, что это должно быть ложно, так как компилятор отклоняет его. Кажется, я хочу позвонить std::runtime_error конструктор прямо из IllegalArgumentException конструктор (ошибка компилятора исчезает, когда я это делаю), но это кажется неправильным, потому что тогда я буду вызывать конструктор для std::runtime_error дважды: один раз в конструкторе для RuntimeExceptionи снова в конструкторе для IllegalArgumentException,

Это безопасно и / или эффективно сделать? Если нет, то почему компилятор, кажется, поощряет это? я мог просто происходит от std::exception и реализовать std::string я как переменная-член, но я подумал, что будет проще получить из стандартного класса, который уже реализовал это. Это неправильный подход? Кроме того, тот факт, что я получаю практически из обоих boost:exception а также std::runtime_error способствуя этой проблеме?

1

Решение

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

Таким образом, конструктор любого из производных классов должен предоставить аргумент virtual база, например:

IllegalArgumentException::IllegalArgumentException(std::string const& what)
: std::runtime_error(what)
, RuntimeException(what) {
}

Чтобы избежать наличия промежуточных баз, вызовите конструктор virtual базовые классы, предназначенные для virtual Наследование часто предоставляют конструктор по умолчанию. Конечно, это открывает возможность того, что самый производный класс неправильно полагается на надлежащий конструктор, вызываемый одной из его баз.

2

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

Других решений пока нет …