Static_assert внутри/вне определения класса

Почему static_assert должен быть вне определения класса?

Ошибка кода

#include <type_traits>

class A
{
public:
    A(A&&) noexcept {}
    static_assert(std::is_nothrow_move_constructible<A>::value, "ERROR");
};

int main()
{
}

Рабочий код

#include <type_traits>

class A
{
public:
    A(A&&) noexcept {}

};

static_assert(std::is_nothrow_move_constructible<A>::value, "ERROR");

int main()
{
}

И когда целесообразно использовать static_asserts в определении класса или структуры?

Ответ 1

Что касается размещения самого static_assert, то обе версии вашего кода действительны. Таким образом, нет, static_assert не должно быть вне определения класса. Формально static_assert - это объявление. Это разрешено везде, где допускаются объявления.

Проблема, с которой вы сталкиваетесь, не имеет ничего общего с static_assert.

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

Обратите внимание, что внутри класса класса объектов члены видны полностью, как полный тип, даже если функция-член определена внутри определения класса. Используя эту функцию, вы можете переписать свой код как

class A
{
public:
    A(A&&) noexcept {
      static_assert(std::is_nothrow_move_constructible<A>::value, "ERROR"); 
    }
};

и std::is_nothrow_move_constructible<A> выдаст правильный результат.