Search code examples
c++memoryconstructorshared-ptrconstexpr

constrexpr constructor inherited from shared_ptr


I want to implement my own pointer (with few helper methods) extended from shared_ptr.

class Event;

class EventPtr : public std::shared_ptr<Event> {
public:
    constexpr EventPtr()
        : std::shared_ptr<Event>() {
    }

    constexpr EventPtr(std::nullptr_t p)
        : std::shared_ptr<Event>(p) {
    }

    explicit EventPtr(Event* ptr)
        : std::shared_ptr<Event>(ptr) {
    }
};

The problem is that compiler gives me the following error for both constexpr constructors: constexpr constructor never produces constant expression

Tell me please how to fix it.


Solution

  • The rules on constexpr constructors changed between C++11 and C++14; see DR1911 constexpr constructor with non-literal base class and this bug.

    The fix is to compile in C++14 mode (-std=c++14).

    Language in C++11 [dcl.constexpr]:

    For a constexpr function, if no function argument values exist such that the function invocation substitution would produce a constant expression (5.19), the program is ill-formed; no diagnostic required. For a constexpr constructor, if no argument values exist such that after function invocation substitution, every constructor call and full-expression in the mem-initializers would be a constant expression (including conversions), the program is ill-formed; no diagnostic required.

    Under C++11, shared_ptr can have constexpr constructors but any class type inheriting from shared_ptr or with a shared_ptr member cannot, because shared_ptr is not a literal type (it has a destructor) and so cannot appear in a constant expression. For C++14 this was simplified to:

    For a non-template, non-defaulted constexpr function or a non-template, non-defaulted, non-inheriting constexpr constructor, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression (5.19), the program is ill-formed; no diagnostic required.

    Unfortunately this makes all constexpr constructors of non-literal types undefined behavior; DR1911 fixed this by adding a subclause (bolded below):

    For a non-template, non-defaulted constexpr function or a non-template, non-defaulted, non-inheriting constexpr constructor, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression (5.20), or, for a constructor, a constant initializer for some object (3.6.2), the program is ill-formed; no diagnostic required.

    struct X { ~X() {} constexpr X() {} };   // OK in C++11, UB in C++14, OK since DR1911
    struct Y : X { constexpr Y() : X() {} }; // UB in C++11, UB in C++14, OK since DR1911