How do I restrict a template class to certain built-in types?

C++20 concept usage

https://en.cppreference.com/w/cpp/language/constraints cppreference is giving as similar example for inheritance as I mentioned at: C++ templates that accept only certain types and by guessing from that I think the syntax for specific classes will be:

template <class T, class Class1, class Class2>
concept Derived = std::is_same<U, Class1>::value || std::is_same<U, Class2>::value;

template<Derived<MyClass1, MyClass2> T>
void f(T);

but I haven't been able to test this yet due to compiler support as explained in that other answer.


One solution I've seen is to use std::enable_if in a type alias. Something like:

using value_type = typename std::enable_if<
                    std::is_same<float, RealType>::value ||
                    std::is_same<double, RealType>::value,
                    RealType
                >::type;

value_type only exists if RealType is exactly float or double. Otherwise, the type is undefined and compilation fails.

I'd warn about being too strict with types, though. Templates are as powerful as they are partly because the duck typing they do means that any type that can be used the way you want to use it, will work. Disallowing types for the sake of disallowing types generally doesn't gain you much, and can make things less flexible than they could be. For example, you wouldn't be able to use a type with more precision, like a big-decimal type.


This way it also allows specialization for various types:

template<typename T, typename Enable = void>
class A {
/// Maybe no code here or static_assert(false, "nice message");
};

/// This specialization is only enabled for double or float.
template<typename T>
class A<T, typename enable_if<is_same<T, double>::value || is_same<T, float>::value>::type> {

};

In your first example, static_assert should take a second parameter which would be a string literal, otherwise it's deemed to fail (edit: dropping the the second parameter is legal since C++17). And this second argument cannot be defaulted.

Your second example is incorrect for several reasons:

  • decltype is meant to be used on an expression, not on a type.
  • You simply cannot compare types with ==, the correct way to do this is what you try in your first attempt with std::is_same.

So, the right way to do what you are trying to achieve is:

#include <type_traits>

template <typename RealType>
class A
{
  static_assert(std::is_same<RealType, double>::value || std::is_same<RealType, float>::value,
                "some meaningful error message");
};

Moreover, I bet you are trying to constrict your template to floating points values. In order to do this, you can use the trait std::is_floating_point:

#include <type_traits>

template <typename RealType>
class A
{
  static_assert(std::is_floating_point<RealType>::value,
                "class A can only be instantiated with floating point types");
};

And as a bonus, take this online example.