Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't "is_base_of" be used inside a class declaration (incomplete type)?

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?

like image 890
user2561762 Avatar asked Feb 06 '18 12:02

user2561762


2 Answers

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.

like image 89
skypjack Avatar answered Oct 29 '22 22:10

skypjack


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.

like image 27
Mateusz Grzejek Avatar answered Oct 29 '22 22:10

Mateusz Grzejek