Home > Software engineering >  Template specialization with only one parameter
Template specialization with only one parameter

Time:06-06

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?

CodePudding user response:

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.
  }
};

CodePudding user response:

It seems difficult to do a partial specification.

Some ways are helpful for you to achieve it:

  1. Define a specification class for this type situation.
  2. If you just only desire to custom this behavior(but not too much and it's limited), you can use if-constexpr branches.
  3. If you want to avoid a class-partial-template (because it's a burden to rewrite all codes), then use a global function template is helpful.

Some suggested codes are given:

#include <iostream> 

template <typename T, unsigned capacity> 
class Collection {
    public: 
    void display(std::ostream &ostr = std::cout) const; 
}; 

template <typename T, unsigned c> 
void Collection<T, c>::display(std::ostream &ostr) const {
    if constexpr (c == 50u) {
        ostr << "Specification! \n"; 
    } else {
        ostr << "Normal Realization. \n"; 
    }
}

int main() {
    Collection<int, 50> c; 
    c.display(); 
}
  • Related