Search code examples
c++templatestemplate-specialization

Template specialization with only one parameter


If you have a class template such as this:

    template <typename T, unsigned CAPACITY>
    class Collection
    {
        T m_array[CAPACITY]{};
        T m_dummy{};
        unsigned m_size{};
    }
    public:
        void display(std::ostream& ostr = std::cout) const 
        {
            ostr << "----------------------" << std::endl;
            ostr << "| Collection Content |" << std::endl;
            ostr << "----------------------" << std::endl;
        }

And I wanted to create specialization depending on the type used, but not the CAPACITY, is this possible?

I have this, which works:

    void Collection<Pair, 50u>::display(std::ostream& ostr) const
    {
        ostr << "----------------------" << std::endl;
        ostr << "| This is a Pair |" << std::endl;
        ostr << "----------------------" << std::endl;
    }

When it is called as: Collection<Pair, 50> colDictionary;

But this only works if the type is Pair, as well as the exact CAPACITY is 50.

This is what I had in mind, allowing for type to be Pair and CAPACITY to be anything:

    void Collection<Pair>::display(std::ostream& ostr) const
    {
        ostr << "----------------------" << std::endl;
        ostr << "| This is a Pair |" << std::endl;
        ostr << "----------------------" << std::endl;
    }

But this causes a "too few arguments for class template" error.

Any way to do this without changing the actual class template itself?


Solution

  • It's called a partial template specialization:

    template <class T, unsigned Capacity>
    struct Collection {
    
    };
    
    template <unsigned Capacity>
    struct Collection<Pair, Capacity> {
      // Specialize
    };
    

    One thing to note is that you cannot partially specialize a single function. You have to specialize the whole class template, which is irritating if the class template is long. Another quick-and-dirty way of doing this if you want to specialize a single function would be to just use a "compile-time if":

    #include <type_traits>
    
    template <class T, unsigned Capacity>
    struct Collection {
      void display() const {
        if constexpr (std::is_same_v<T, Pair>) {
          // pair implementation
        } else {
          // general implementation
        }
      }
    };
    

    Or, as a more clean solution, try moving the whole thing out of the class and add a simple overload:

    // Free-standing overloads:
    
    template <class T, unsigned Capacity>
    void diplay(Collection<T, Capacity> const& c) { /* ... */ }
    
    template <unsigned Capacity>
    void display(Collection<Pair, Capacity> const& c) { /* ... */ }
    
    
    // The member function delegates the work to
    // the overloaded functions. No template specialization
    // is involved:
    
    template <class T, unsigned Capacity>
    struct Capacity {
      void display() const {
        display(*this); // calls the correct overload.
      }
    };