Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initializing a static constexpr from an incomplete type because of a template base class

I have a template base class, whereby subclasses are expected to pass themselves as the template parameter.

It looks a little like this:

template<typename T>
struct Base {
    constexpr Base(int x) : m_x(x) {}
private:
    int m_x;
};

struct Derived : public Base<Derived>
{
    static const Derived LIFE;
    constexpr Derived(int x) : Base(x) {}
};

const Derived Derived::LIFE = Derived(42);

That compiles and works as expected. But now I'd like to make Derived::LIFE a constexpr. Is this even possible?

I can't just change it's const qualifier to constexpr, as a constexpr needs to be initialized in its declaration:

test.cpp:10:28: error: constexpr static data member ‘LIFE’ must have an initializer
   static constexpr Derived LIFE;

I can't initialize it there since Derived is an incomplete type:

test.cpp:10:45: error: invalid use of incomplete type ‘struct Derived’
   static constexpr Derived LIFE = Derived(42);

I realize this problem would go away if Derived were a complete type, but I'm pretty attached to the self-referential templated base class in this particular case for reasons not relevant to this question.

If I understand the last paragraph in this answer correctly, it sounds like there is at least some discussion about changing the way incomplete types are handled at some point in the future, but that doesn't help me now.

Is anyone aware of some sort of trick to defer the initialization of LIFE in my above code?

like image 533
Martin Avatar asked Dec 16 '22 02:12

Martin


2 Answers

You can simply add constexpr to the definition of LIFE:

constexpr Derived Derived::LIFE = Derived(42);

Until recently GCC had a bug where it rejected this; you'll need to use either Clang or GCC 4.9.

like image 144
Richard Smith Avatar answered Dec 27 '22 11:12

Richard Smith


I think you shall use lazy initialization. Actually Derived is still incomplete type; because the compiler don't know its size yet.

So the code shall be:

struct Derived : public Base<Derived>
{
    constexpr Derived(int x) : Base(x) {}

    static constexpr Derived getLIFE()
    {
        return Derived(34);
    }
};

EDIT: Same incomplete type behavior can be reproduced using this snippet:

struct MyStruct
{
    static constexpr int x = sizeof(MyStruct);
};
like image 20
Yousf Avatar answered Dec 27 '22 10:12

Yousf