Search code examples
c++templatesc++20sfinaeclass-template

How to restrict generic class method template parameter to certain types?


I have checked out std::enable_if to conditionally compile a member function

However it doesn't work for me. I need to restrict T of a class method to some types.

template<typename T = typename enable_if_t<
    is_same_v<T, long> || is_same_v<T, int> || is_same_v<T, double> 
    || is_same_v<T, float> || is_same_v<T, size_t>>
    >
shared_ptr<Node<T>> LinkedList<T>::AddNumbers(
    shared_ptr<Node<T>> p1, shared_ptr<Node<T>> p2, T carry)
{
    <snip>
}

I get build error:

identifier T is undefined

I am using C++20. Any advice and insight is appreciated.


I try out concepts suggested by @JeJo, but get the following build error on the line performing arithmetics:

error C2676: binary '/': 'T' does not define this operator or 
a conversion to a type acceptable to the predefined operator

I have the template class declaration in header file and implementation in .cpp file. Header file:

template <typename T> class LinkedList
{
public:
    <snip>
    shared_ptr<Node<T>> AddNumbers(
           shared_ptr<Node<T>>, shared_ptr<Node<T>>, T carry = 0);
};

When I use the suggestion by @JeJo, I bump into

error C3855: 'LinkedList<T>': template parameter 'T' is
             incompatible with the declaration

Solution

  • Despite what the other answers say, the member function doesn't need to (and shouldn't) be a template, assuming you use requires. That's only necessary when you use the classical SFINAE.

    #include <cstddef>
    #include <iostream>
    #include <memory>
    #include <type_traits>
    
    template <typename T, typename ...P>
    concept one_of = (std::is_same_v<T, P> || ...);
    
    template <typename T>
    struct Node {};
    
    template <typename T>
    class LinkedList
    {
    public:
        std::shared_ptr<Node<T>> AddNumbers(std::shared_ptr<Node<T>>, std::shared_ptr<Node<T>>, T carry = 0)
        requires one_of<T, int, long, std::size_t, float, double>
        {
            // ...
        }
    };
    
    int main()
    {
        LinkedList<int> s;
        s.AddNumbers(nullptr, nullptr, 0);
    
        LinkedList<char> t;
        // t.AddNumbers(nullptr, nullptr, 0);
    }
    

    Any boolean condition can be spelled after requires, but I've added a concept for brevity.