Question

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?

Was it helpful?

Solution

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

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top