14

I completely see why this cannot work:

class Base {};
class A;
static_assert(std::is_base_of<Base, A>::value, "");

Because there is no information about a 'class hierarchy', but... Why cannot the following work?

class Base {};
class A : public Base {
    static_assert(std::is_base_of<Base, A>::value, "");
};
(produce: an undefined class is not allowed as an argument to compiler intrinsic type trait)

The type 'A' is still not complete at line with static_assert (according to definition of this concept). However - the compiler already knows the 'class hierarchy' and could provide the answer for this.

Of course - this static_assert could be moved to destructor or whatever to fix this issue, but there are situations where it cannot be done, for example:

class Base {};

template<typename T>
struct type_of {
    static_assert(std::is_base_of<Base, T>::value, "T is not derived from Base");
    using type = int; //* Some normal type in real use
};

class A : public Base {
public:
    type_of<A>::type foo(); // Will not compile
};

Should it not be allowed?

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
user2561762
  • 353
  • 1
  • 8

2 Answers2

18

A class definition is complete (that is, a class is considered defined) after the closing brace }.
In your case, when you try to use A with std::is_base_of, A isn't fully defined yet:

class A : public Base {
    // no closing brace for A yet, thus A isn't fully defined here
    static_assert(std::is_base_of<Base, A>::value, "");
};

On the other side, std::is_base_of requires types that are completely defined to work.
Thus the error.


As a workaround, you can put the assert in the destructor of A:

class A : public Base {
    ~A() {
        static_assert(std::is_base_of<Base, A>::value, "");
    }
};

In fact, a class type is considered fully defined in its member functions' bodies.


See here for more details (emphasis mine):

A class is considered a completely-defined object type ([basic.types]) (or complete type) at the closing } of the class-specifier. Within the class member-specification, the class is regarded as complete within function bodies, default arguments, noexcept-specifiers, and default member initializers (including such things in nested classes). Otherwise it is regarded as incomplete within its own class member-specification.

skypjack
  • 45,296
  • 16
  • 80
  • 161
  • 2
    " _a class type is considered fully defined in its member functions' bodies_ " ... that's a good, insightful point! – Eljay Feb 06 '18 at 13:27
  • 1
    See [here](http://eel.is/c++draft/class#mem-6) - _A class is considered a completely-defined object type ([basic.types]) (or complete type) at the closing } of the class-specifier. Within the class member-specification, the class is regarded as complete within function bodies, default arguments, noexcept-specifiers, and default member initializers (including such things in nested classes). Otherwise it is regarded as incomplete within its own class member-specification._ – skypjack Feb 06 '18 at 13:29
7

The doc page for std::is_base_of yields:

If both Base and Derived are non-union class types, and they are not the same type (ignoring cv-qualification), Derived shall be a complete type; otherwise the behavior is undefined.

The definition for a class is complete after you close its scope with }. Thus, in your case, an error is generated.


Note, that static assertion can appear in block or namespace / file scope. So you can move it outside the class' body or, if you don't want to have it in your header, move to the implementation file.

Mateusz Grzejek
  • 10,635
  • 3
  • 30
  • 47