Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is a class definition complete when it is passed to a base class?

I am trying to write a CRTP to contain a static constexpr of the derived type, since this is impossible to do with just one class. This code compiles fine in GCC, but clang complains that Derived is an incomplete type. Which one is right?

template<class T>
class Base {
public:
    static constexpr T a = T(1), b = T(20);
};

class Derived : public Base<Derived> {
public:
    int x;
    constexpr Derived(int x) : x(x) {}
};
like image 239
user975989 Avatar asked Nov 05 '15 14:11

user975989


1 Answers

Derived is incomplete at the point at which Base<Derived> is instantiated ([class.mem]/2), which happens right at definition time. You used constexpr, which necessitates an initializer as per [class.static.data]/3, and when Base<Derived> is instantiated, so are the declarations of its static data members ([temp.inst]/3), which includes initializers. However, the initializers are attempting to create an object of incomplete type, which is ill-formed.

You can declare your member as const instead:

template<class T>
class Base {
public:
    static const T a;
};
template <typename T>
constexpr T Base<T>::a = T(1);

since the initializer is now at the definition, the instantiation of this initializer can be deferred until e.g. Derived is complete. Demo with Clang.

Note that Clang doesn't yet treat a as constexpr because it fails to eagerly instantiate its definition. See bug #24541.

like image 125
Columbo Avatar answered Nov 04 '22 17:11

Columbo