A safe, standard-compliant way to make a class template specialization fail to compile using `static_assert` only if it is instantiated?

Static assertions are there to be used directly in the class without doing anything complicated.

#include <type_traits>

template<typename T>
struct OnlyNumbers {
    static_assert(std::is_arithmetic_v<T>, "T is not arithmetic type.");
    // ....
};

In some cases, you might get additional error messages since instanciating OnlyNumbers for non-arithmetic types might cause more compilation errors.

One trick I have used from time to time is

#include <type_traits>

template<typename T>
struct OnlyNumbers {
    static_assert(std::is_arithmetic_v<T>, "T is not arithmetic type.");
    using TT = std::conditional_t<std::is_arithmetic_v<T>,T,int>;
    // ....
};

In this case, your class gets instanciated with int, a valid type. Since the static assertion fails anyway, this does not have negative effects.