Почему в объявлении класса (неполного типа) нельзя использовать "is_base_of"?

Я полностью понимаю, почему эта не может работать:

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

Потому что нет никакой информации об иерархии классов, но... Почему не может работать следующее?

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)

Тип "A" по-прежнему не завершен в строке с static_assert (согласно определению этого понятия). Однако - компилятор уже знает "иерархию классов" и может предоставить ответ для этого.

Конечно - этот static_assert может быть перемещен в деструктор или что-то еще, чтобы исправить эту проблему, но бывают ситуации, когда это невозможно сделать, например:

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

Если это не разрешено?

Ответ 1

Определение класса завершено (то есть, класс считается определенным) после замыкающей скобки }.
В вашем случае, когда вы пытаетесь использовать A с std::is_base_of, A еще не определено полностью:

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

С другой стороны, std::is_base_of требует типов, которые полностью определены для работы.
Таким образом, ошибка.


В качестве обходного пути вы можете поместить утверждение в деструктор A:

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

Фактически, тип класса считается полностью определенным в его телах функций-членов.


Подробнее см. здесь для более подробной информации (акцент мой):

Класс рассматривается как полностью определенный тип объекта ([basic.types]) (или полный тип) при закрытии} спецификатора класса. В классе-член класса класс считается полным в телах функций, аргументах по умолчанию, спецификаторах noexcept и инициализаторах по умолчанию (включая такие вещи во вложенных классах). В противном случае он считается неполным в пределах своей спецификации класса.

Ответ 2

Страница doc для std::is_base_of дает:

Если оба Base и Derived являются типами неединичных классов, и они не являются тот же тип (игнорируя cv-квалификацию), Производный должен быть полным тип; в противном случае поведение undefined.

Определение для класса завершено после закрытия его области с помощью }. Таким образом, в вашем случае генерируется ошибка.


Обратите внимание, что static assertion может отображаться в области пространства или пространства имен/файлов. Таким образом, вы можете переместить его за пределы тела класса или, если вы не хотите иметь его в своем заголовке, перейдите к файлу реализации.