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

27,534

Solution 1

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.

Solution 2

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.

Solution 3

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> {

};

Solution 4

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.

Share:
27,534
quant
Author by

quant

I'm here to learn programming and *nix mostly, and am immensely indebted to this community for always being there when I get stuck. The help and support I've received on SE sites has allowed me to develop my understanding in a range of disciplines much faster than I could ever have hoped for. I try to give back once in a while as well; it's the least I could do. I'm the owner and maintainer of www.portnovel.com

Updated on May 09, 2020

Comments

  • quant
    quant almost 4 years

    This issue has been discussed a few times but all the solutions I have found either didn't work or were based on boost's static assert. My problem is simple. I have a class, and I only want to allow real types (double and float). I want a compile-time error if I try to instantiate the class with a type other than float or double. I am using Visual C++ 11. Here is what I have tried:

    template <typename RealType>
    class A
    {
      // Warning C4346
      static_assert(std::is_same<RealType, double>::value || std::is_same<RealType, float>::value);
    }
    
    
    template <typename RealType>
    class A
    {
      // Error C2062: type 'unknown' unexpected
      static_assert(decltype(RealType) == double || decltype(RealType) == float);
    }
    

    Any ideas? Thanks in advance!