Lærne
Lærne

Reputation: 3142

constexpr constructor cannot be used in constexpr constructor

I want to redefine unique_ptr with a special destructor. Therefore I use the following code where I try to mimic some constructors of unique_ptr. Unfortunately constexpr constructors refuse to build and I have no idea why.

class job_ptr : public unique_ptr<Job>
{
public:
    constexpr job_ptr()
        : unique_ptr<Job>(), sequencer( nullptr ) {}
    constexpr job_ptr( nullptr_t )
        : unique_ptr<Job>( nullptr ), sequencer( nullptr ) {}
private:
    FIFOSequencer* sequencer;
};

Both constructors in the initialisation list are declared constexpr, however clang++ considers that constexpr constructor never produces a constant expression because non-literal type 'unique_ptr<Job>' cannot be used in a constant expression. What does it try to mean ? constexpr constructors cannot be used within constexpr constructors ?

Thank you for any help.

Upvotes: 0

Views: 3916

Answers (1)

Johan
Johan

Reputation: 3778

Constexpr constructors are possibles, but the requirement are quite strict. The main problem for you is, as @dyp said, that std::unique_ptr as no trivial destructor and thus is not a LiteralType.

If you try with an int under g++:

class int_ptr : public std::unique_ptr<int>
{
public:
    constexpr int_ptr()
        : std::unique_ptr<int>(), sequencer( nullptr ) {}
    constexpr int_ptr( nullptr_t )
        : std::unique_ptr<int>( nullptr ), sequencer( nullptr ) {}
private:
    int* sequencer;
};

constexpr int_ptr ptr;

You have a very explicit error message :

unique_ptr.cpp:40:20: error: the type ‘const int_ptr’ of constexpr variable ‘ptr’ is not literal
  constexpr int_ptr ptr;
                    ^
unique_ptr.cpp:27:7: note: ‘int_ptr’ is not literal because:
 class int_ptr : public std::unique_ptr<int>
       ^
unique_ptr.cpp:27:7: note:   ‘int_ptr’ has a non-trivial destructor

In your case, as suggested in the comment, use a custom deleter. STL container are not really well suited for inheritance.

Here an example of a custom deleter:

#include <memory>
#include <iostream>

template <typename T>
struct Deleter
{
    void operator()(T* t)
    {
        std::cout << "Deleter::oerator(): " << t << std::endl;
        delete t;
    }
};

struct A 
{
    A() 
    {
        std::cout << "A::A()" << std::endl;
    }

    ~A() 
    {
        std::cout << "A::~A()" << std::endl;
    }
};

int main(int argc, char const *argv[])
{
    std::unique_ptr<A, Deleter<A>> ptr(new A);


    return 0;
}

It outputs:

A::A()
Deleter::oerator(): 0x600010480
A::~A()

(live run

Upvotes: 5

Related Questions