0
votes

I'm don't understand why template specialization is different for variadic templates than for regular (i.e., non-variadic) templates. For example I have a template and a specialization like:

enum class MF : int {
    ZERO = 0,
    ONE = 1,
    TWO = 2
};

// --------- Specialization -------------
template <MF mf>
class Stat{
public:
    Stat(std::string msg) {
        cout << "Generic Stat construtor: " << msg << endl;
    }
};

// --------- Template Specialization -------------
template<>
class Stat<MF::ONE>{
public:
    Stat(std::string msg) {
        cout << "Specialized Stat constructor: " << msg << endl;
    }
};

I have specialized with a specific value of the MF enumeration.

Now if I want to specialize a variadic template I can't specialize the variadic template parameters with a specific value of the MF enumeration (e.g., MF::ONE), I can only specialize with a type, (e.g. MF).

// --------- Variadic Template -------------
template<MF mf, typename... E>
class Var{
public:
    Var(std::string msg){
        cout << "Generic Var constructor: " << msg << endl;
    }

};

// --------- Variadic Template Specialization -------------
template<>
class Var<MF::TWO, MF>{
public:
    Var(std::string msg){
        cout << "Specialized Var constructor: " << msg << endl;
    }
};

I would like to specialize my variadic template for a specific MF value, but it doesn't appear that I can.

Is there some aspect of the language that I'm missing that would allow me to do what I want? Something along the lines of:

template<>
class Var<MF::TWO, MF::ONE>{
public:
    Var(std::string msg){
        cout << "Specialized Var constructor: " << msg << endl;
    }
};

Complete example can be found here

2
but it doesn't appear that I can. do you get an error or anything?Sebastian Hoffmann
Is MF::ONE a typename? Then, why do you expect to be able to pass it to a typename...?Yakk - Adam Nevraumont
MF::ONE is an enumerate value.jlconlin
@Paranaix the code won't compile if I specialize like class Var<MF::TWO, MF::ONE>. I get this error error: template argument for template type parameter must be a type class Var<MF::TWO, MF::ONE>{jlconlin

2 Answers

3
votes

templates are not macros. typename... does not mean "take any number of comma delimited strings". typename... means "take 0 or more types".

MF::ONE is not a type.

Attempting to pass MF::ONE where the template expects typename... is an error, because the template is asking for a typename not a value.

You can write a template class that takes 0 or more MFs. You can write one that takes zero or more types. You cannot write a template class that takes 0 or more MFs or types. C++ does not support that.

If you must pass values via type, there is std::integral_constant< TYPE, VALUE >, which is a type that wraps an integral constant value.

template<MF mf, typename... E>
class Var;
template<>
class Var<MF::TWO, std::integral_constant<MF, MF::ONE>>{
public:
  Var(std::string msg){
    cout << "Specialized Var constructor: " << msg << endl;
  }
};

you'd instantiate this by Var<MF::TWO, std::integral_constant<MF, MF::ONE>>.

You can create aliases:

template<MF mf> using MF_t=std::integral_constant<MF, mf>;

to make your code look nicer:

template<MF mf, typename... E>
class Var;
template<>
class Var<MF::TWO, MF_t<MF::ONE>>{
public:
  Var(std::string msg){
    cout << "Specialized Var constructor: " << msg << endl;
  }
};
int main() {
  Var<MF::TWO, MF_t<MF::ONE>> instance("hello");;
}

For metaprogramming purposes, sometimes it is a wise idea to write your templates as always taking types, and never taking constants, and when you need a constant stuff it into a type like above. Then template<class...>class X will match any such template.

You can gain access to the value of a std::integral_constant with ::value or by constructing it and converting it to a value of that type. (In C++1y and many C++11 compilers, that conversion is constexpr).

So

 MF x = MF_t<MF::ONE>{};
 MF y = MF_t<MF::ONE>::value;

will set x and y to MF::ONE. In both cases, the right hand side should be a compile time expression, so:

MF_t< MF_t<MF::ONE>{} >

is a valid type (if a silly written one) that is the same as MF_t< MF::ONE >.

4
votes

If I understood you correctly, you want your variadic template to be parameterized with any number of MF values. Well, don't declare it like this :

template<MF mf, typename... E>

... which declares any number of type parameters, but like this :

template<MF mf, MF... moreMF>

... which declares any number of non-type parameters of type MF.

That is to say, explicit specialisation is no different for variadic templates, but you somehow forgot how to parameter in-between.