3
votes

The following basic example of partial template class specialization, taken from this wiki page :

template <typename Key, typename Value>
class KeyValuePair { /* something generic */ };

template <typename Key>
class KeyValuePair<Key, bool> { /* something specific to Value=bool */ };

KeyValuePair<int> kvpi;

generates a compiler error :

prog.cpp:10:17: error: wrong number of template arguments (1, should be 2)  KeyValuePair<int> kvpi;

Why ? what am I doing wrong ? How a partial template class specialization should be declared and instantiated ?

I am expecting variable kvpi to be a partially specialized template class instance of type KeyValuePair<int,bool>.

3
What do you expect Value to be in KeyValuePair<int>? - TartanLlama
@TartanLlama: In KeyValuePair<int>, I expect Value to be bool, but I did manifestly not define the partial specialization properly to do so. - shrike
@shrike Partial specialization doesn't work the way you are expecting it to work. You will have to provide both Key and Value type. Only when the value type is bool, it will choose the specialized class implementation, for all other types it will use the first implementation. - Arunmu
@shrike Sounds like you want a default template argument rather than partial specialization: template <typename Key, typename Value = bool> class KeyValuePair {}; - TartanLlama
OK, understood, I was confusing partial specialization and default parameter; actually, I need both. Thanks for pointing what was wrong. - shrike

3 Answers

6
votes

You appear to be confusing partial specialization with default template arguments. It further appears you need both (for reasons unstated, but not really important). Though not entirely intuitive, it can be accomplished as follows:

#include <iostream>

template <typename Key, typename Value = bool>
class KeyValuePair
{
public:
    KeyValuePair()
    {
        std::cout << __PRETTY_FUNCTION__ << ':' << "original\n";
    }
};

template <typename Key>
class KeyValuePair<Key, bool>
{
public:
    KeyValuePair()
    {
        std::cout << __PRETTY_FUNCTION__ << ':' << "specialized\n";
    }
};


int main()
{
    KeyValuePair<int,int> kvp1;
    KeyValuePair<int> kvp2;
}

Output

KeyValuePair<int, int>::KeyValuePair() [Key = int, Value = int]:original
KeyValuePair<int, bool>::KeyValuePair() [Key = int, Value = bool]:specialized

The confusing part to some is the default argument specification, but a template that follows which will never actually see fruition with that default-arg because of the later specialization. In such cases I prefer to forward-declare the template with its default argument list. At least for me it makes it slightly easier to read. You may (or not) choose to do the same if you feel it offers clarity. Something like:

template <typename Key, typename Value = bool>
class KeyValuePair;

template <typename Key, typename Value>
class KeyValuePair
{
public:
    KeyValuePair()
    {
        std::cout << __PRETTY_FUNCTION__ << ':' << "original\n";
    }
};

template <typename Key>
class KeyValuePair<Key, bool>
{
public:
    KeyValuePair()
    {
        std::cout << __PRETTY_FUNCTION__ << ':' << "specialized\n";
    }
};
4
votes

Partial template specialization doesn't allow you to forget a template parameter. You have to write:

KeyValuePair<int, bool> kvpib;

and correct specialization will be used.

But you can use inheritance to achieve what you want:

template <typename Key>
class KeyBoolPair : public KeyValuePair<Key, bool> {};

KeyBoolPair<int> kbpi;

or if you use C++11 or greater:

template <typename Key>
using KeyBoolPairBis = KeyValuePair<Key, bool>;

KeyBoolPairBis<int> kbpbi;

example: http://coliru.stacked-crooked.com/a/5095fc5c358d291a

3
votes

You define your partial specialization correctly, but the template expect 2 argument and you provide only one.

You may use default argument:

template <typename Key, typename Value = bool>
class KeyValuePair {};

to allow

KeyValuePair<int> kvpi;

And you may keep your partial specialization if needed

template <typename Key>
class KeyValuePair<Key, bool> { /* something specific to Value=bool */ };