Определите, является ли конструктор абстрактного базового класса noexcept?
В C++11 и более поздних версиях, как определить, является ли конструктор абстрактного базового класса noexcept
? Следующие методы не работают:
#include <new>
#include <type_traits>
#include <utility>
struct Base { Base() noexcept; virtual int f() = 0; };
// static assertion fails, because !std::is_constructible<Base>::value:
static_assert(std::is_nothrow_constructible<Base>::value, "");
// static assertion fails, because !std::is_constructible<Base>::value:
static_assert(std::is_nothrow_default_constructible<Base>::value, "");
// invalid cast to abstract class type 'Base':
static_assert(noexcept(Base()), "");
// invalid new-expression of abstract class type 'Base'
static_assert(noexcept(new (std::declval<void *>()) Base()), "");
// cannot call constructor 'Base::Base' directly:
static_assert(noexcept(Base::Base()), "");
// invalid use of 'Base::Base':
static_assert(noexcept(std::declval<Base &>().Base()), "");
Простое использование для этого было бы:
int g() noexcept;
struct Derived: Base {
template <typename ... Args>
Derived(Args && ... args)
noexcept(noexcept(Base(std::forward<Args>(args)...)))
: Base(std::forward<Args>(args)...)
, m_f(g())
{}
int f() override;
int m_f;
};
Есть ли идеи о том, как это архивировать или возможно ли вообще без изменения абстрактного базового класса?
PS: любые ссылки на отчеты о дефектах ISO C++ или незавершенном производстве также приветствуются.
EDIT: как было указано дважды, по умолчанию Derived
конструкторы с = default
делают noexcept
наследуемыми. Но это не решает проблему для общего случая.
4 ответа:
[ОБНОВЛЕНИЕ: СТОИТ ПЕРЕЙТИ К РАЗДЕЛУ EDIT
Хорошо, я нашел решение, хотя оно не компилируется со всеми компиляторами из-за ошибки в GCC (смотрите Этот вопрос для более подробной информации).
Решение основано на унаследованных конструкторах и способе разрешения вызовов функций.
Рассмотрим следующий пример:#include <utility> #include <iostream> struct B { B(int y) noexcept: x{y} { } virtual void f() = 0; int x; }; struct D: public B { private: using B::B; public: template<typename... Args> D(Args... args) noexcept(noexcept(D{std::forward<Args>(args)...})) : B{std::forward<Args>(args)...} { } void f() override { std::cout << x << std::endl; } }; int main() { B *b = new D{42}; b->f(); }
Я думаю, это совершенно ясно.
В любом случае, дайте мне знать, если вы обнаружите, что что-то нуждается в большем подробности и я буду рад обновить ответ.
Основная идея заключается в том, что мы можем непосредственно наследовать определениеnoexcept
от базового класса вместе с его конструкторами, так что нам больше не нужно ссылаться на этот класс в наших операторахnoexcept
.Здесь вы можете увидеть вышеупомянутый рабочий пример.
[EDIT]
Как и из комментариев, пример страдает от проблемы, если конструкторы базового класса и производного имеют одинаковое значение. подпись.
Спасибо Петру Скотницкому, что указал на это.
Я собираюсь упомянуть эти комментарии, и я скопирую и вставлю предложенный код вместе с ними (с упоминанием авторов, где это необходимо).Прежде всего, здесь мы видим, что пример как он есть не работает так, как ожидалось (спасибо Петру Скотницкому за ссылку).
Код почти такой же, как и ранее, поэтому его не стоит копировать и вставлять сюда.
Кроме того, от того же автора, далее следует пример , который показывает, что одно и то же решение работает, как и ожидалось, при определенных обстоятельствах (см. комментарии для деталей furter):Кроме того, я предлагаю альтернативное решение, которое немного более навязчиво и основано на идее, за которую выступает#include <utility> #include <iostream> struct B { B(int y) noexcept: x{y} { std::cout << "B: Am I actually called?\n"; } virtual void f() = 0; int x; }; struct D: private B { private: using B::B; public: template<typename... Args> D(int a, Args&&... args) noexcept(noexcept(D{std::forward<Args>(args)...})) : B{std::forward<Args>(args)...} { std::cout << "D: Am I actually called?\n"; } void f() override { std::cout << x << std::endl; } }; int main() { D* d = new D{71, 42}; (void)d; }
std::allocator_arg_t
, которая также предложена Петром Скотницким в комментарии:Достаточно, если конструктор производного класса выигрывает в разрешении перегрузки.
Он следует коду упоминается здесь :
Еще раз спасибо Петру Скотницкому за помощь и комментарии, очень благодарен.#include <utility> #include <iostream> struct B { B(int y) noexcept: x{y} { std::cout << "B: Am I actually called?\n"; } virtual void f() = 0; int x; }; struct D: public B { private: using B::B; public: struct D_tag { }; template<typename... Args> D(D_tag, Args&&... args) noexcept(noexcept(D{std::forward<Args>(args)...})) : B{std::forward<Args>(args)...} { std::cout << "D: Am I actually called?\n"; } void f() override { std::cout << x << std::endl; } }; int main() { D* d = new D{D::D_tag{}, 42}; (void)d; }
На основе ответаskypjack лучшим решением, не требующим изменения сигнатуры конструктора
Derived
, было бы определение фиктивного подклассаBase
как частного члена типаDerived
и использование его конструкции в спецификацииDerived
конструктораnoexcept
:class Derived: Base { private: struct MockDerived: Base { using Base::Base; // Override all pure virtual methods with dummy implementations: int f() override; // No definition required }; public: template <typename ... Args> Derived(Args && ... args) noexcept(noexcept(MockDerived(std::forward<Args>(args)...))) : Base(std::forward<Args>(args)...) , m_f(g()) {} int f() override { return 42; } // Real implementation int m_f; };
Наивным, но работающим примером было бы введение невиртуального базового класса и экспорт его конструктора с помощью директивы
using
. Вот пример:#include<utility> struct BaseBase { BaseBase() noexcept { } }; struct Base: public BaseBase { using BaseBase::BaseBase; virtual int f() = 0; }; struct Derived: public Base { template <typename ... Args> Derived(Args && ... args) noexcept(noexcept(BaseBase(std::forward<Args>(args)...))) : Base(std::forward<Args>(args)...) { } int f() override { } }; int main() { Derived d; d.f(); }
Я столкнулся с той же проблемой,и решение, которое я нашел, состояло в реализации дополнительных черт.
Вы можете посмотреть на мой пост здесь.