Consider the following class, with the inner struct Y
being used as a type, eg. in templates, later on:
template
class X{
templat
You can use a meta function (here: inlined boost::mpl::if_c
, but could be arbitrarily complex) to select the one you want. You need some scaffolding to be able to use constructors, though:
template <int I>
class X {
template <typename T1>
class YforIeq1 { /* meat of the class */ };
template <typename T1, typename T2>
class YforIeq2 { /* meat of the class */ };
public:
template <typename T1, typename T2=boost::none_t/*e.g.*/>
struct Y : boost::mpl::if_c<I==1,YforIeq1<T1>,YforIeq2<T1,T2> >::type {
typedef typename mpl::if_c<I==1,YforIeq1<T1>,YforIeq2<T1,T2> >::type YBase;
/* ctor forwarding: C++0x */
using YBase::YBase;
/* ctor forwarding: C++03 (runs into perfect fwd'ing problem)*/
Y() : YBase() {}
template <typename A1>
Y(const A1&a1) : YBase(a1) {}
template <typename A1, typename A2>
Y(const A1&a1, const A2&a2) : YBase(a1,a2) {}
// ...
};
};
If there's a problem with both YforIeq
N being instantiated for each X, then you can try wrapping them as a nullary meta function (something along the way mpl::apply
does) and use mpl::eval_if_c
.
Here you go:
http://ideone.com/AdEfl
And the code:
#include <iostream>
template <int I>
struct Traits
{
struct inner{};
};
template <>
struct Traits<1>
{
struct inner{
template<class T1>
struct impl{
impl() { std::cout << "impl<T1>" << std::endl; }
};
};
};
template <>
struct Traits<2>
{
struct inner{
template<class T1, class T2>
struct impl{
impl() { std::cout << "impl<T1, T2>" << std::endl; }
};
};
};
template<class T>
struct Test{};
template<class T, class K>
struct Foo{};
template<int I>
struct arg{};
template<
template<class, class> class T,
class P1, int I
>
struct Test< T<P1, arg<I> > >{
typedef typename Traits<I>::inner inner;
};
template<
template<class, class> class T,
class P2, int I
>
struct Test< T<arg<I>, P2 > >{
typedef typename Traits<I>::inner inner;
};
// and a bunch of other partial specializations
int main(){
typename Test<Foo<int, arg<1> > >::inner::impl<int> b;
typename Test<Foo<int, arg<2> > >::inner::impl<int, double> c;
}
Explanation: Basically it's an extension of the idea of partial specialization, however the difference is that rather than specializing within Test
, delegate to a specific class that can be specialized on I
alone. That way you only need to define versions of inner
for each I
once. Then multiple specializations of Test
can re-use. The inner
holder is used to make the typedef
in the Test
class easier to handle.
EDIT: here is a test case that shows what happens if you pass in the wrong number of template arguments: http://ideone.com/QzgNP
Can you try below (it is not partial specialization):
template<int I>
class X
{
};
template<>
class X<1>
{
template<class T1>
struct Y{};
};
template<>
class X<2>
{
template<class T1, class T2>
struct Y{};
};
I doubt if the answer is that simple !!
Edit (Mocking Partial specialization): @Xeo, I was able to compile following code and seems to be fullfilling.
template<int I>
struct X
{
struct Unused {}; // this mocking structure will never be used
template<class T1, class T2 = Unused> // if 2 params passed-->ok; else default='Unused'
struct Y{};
template<class T1>
struct Y<T1, Unused>{}; // This is specialization of above, define it your way
};
int main()
{
X<1>::Y<int> o1; // Y<T1 = int, T2 = Unused> called
X<2>::Y<int, float> o2; // Y<T1 = int, T2 = float> called
}
Here, however you can use X<1>, X<2> interchangeably. But in the broader example you mentioned, that is irrelevant. Still if you need, you can put checks for I = 1
and I = 2
.
How about this approach - http://sergey-miryanov.blogspot.com/2009/03/template-class-overriding.html? (sorry for russian)
There are two problems here:
enable_if
works with partial specialization, not primary templates.As you suggested in chat, a linked list of templates can emulate the variadic parameter pack.
template<int I>
class X{
template<class list, class = void>
struct Y;
template<class list>
struct Y< list, typename std::enable_if<I==1>::type > {
typedef typename list::type t1;
};
template<class list>
struct Y< list, typename std::enable_if<I==2>::type > {
typedef typename list::type t1;
typedef typename list::next::type t2;
};
};
If you end up with next::next::next
garbage, it's easy to write a metafunction, or use Boost MPL.
The different-arity templates can be named similarly but still stay distinct if they are nested inside the SFINAE-controlled type.
template<int I>
class X{
template<typename = void, typename = void>
struct Z;
template<typename v>
struct Z< v, typename std::enable_if<I==1>::type > {
template<class T1>
struct Y{};
};
template<typename v>
struct Z< v, typename std::enable_if<I==2>::type > {
template<class T1, class T2>
struct Y{};
};
};
X<1>::Z<>::Y< int > a;
X<2>::Z<>::Y< char, double > b;