Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine whether a constructor of an abstract base class is noexcept?

In C++11 and later, how to determine whether a constructor of an abstract base class is noexcept? The following methods don't work:

#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()), "");

A simple use for this would be:

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;
};

Any ideas about how to archieve this or whether it is possible at all without modifying the abstract base class?

PS: Any references to ISO C++ defect reports or work-in-progress is also welcome.

EDIT: As was pointed out twice, defaulting the Derived constructors with = default makes noexcept being inherited. But this does not solve the problem for the general case.

like image 457
jotik Avatar asked Mar 03 '16 16:03

jotik


2 Answers

A naive but working example would be to introduce a non-virtual base class and export its constructor by means of the using directive. Here is an example:

#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();
}
like image 141
skypjack Avatar answered Nov 02 '22 19:11

skypjack


Based on skypjack's answer a better solution which does not require a signature change of the Derived constructor would be to define a mock subclass of Base as a private type member of Derived, and use the construction of that in the Derived constructor noexcept specification:

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;

};
like image 5
jotik Avatar answered Nov 02 '22 19:11

jotik