Yi.He
Yi.He

Reputation: 69

Ambiguous partial template specialization with variadic template

For the following code:

template <typename T>
struct IsOneOf<T,T> { const static bool True = true; };
template <typename T, typename ... Ts>
struct IsOneOf<T,any_of<T,Ts ...> > { const static bool True = true;};
template <typename T, typename T2,typename ... Ts>
struct IsOneOf<T,any_of<T2,Ts ...> > {
  const static bool True = IsOneOf<T,T2>::True ||
                           IsOneOf<T,any_of<Ts ...> >::True;
};
template <typename T1, typename ... Ts1, typename ... Ts2>
struct IsOneOf< any_of<T1,Ts1 ...>, any_of<Ts2 ...> > {
  const static bool True = IsOneOf<T1,any_of<Ts2 ...> >::True &&
                           IsOneOf<any_of<Ts1...>, any_of<Ts2 ...> >::True;
};

The last specialization is intended to override the others, but I receive ambiguous template instantiation errors for IsOneOf<any_of<int>,any_of<int,double,float>>::True. Can someone suggest a way to overcome this?

Upvotes: 2

Views: 141

Answers (2)

Walter
Walter

Reputation: 45434

What's the point of this complicated and opaque design? If the idea was that IsOneOf<A,B...>::value is true if and only if at least one of the B types is the same as A, then a simple recursive design (avoiding auxiliaries such as any_of) such as

// is_one_of<A,B1,B2,...>::value == is_same<A,B1>::value || is_same<A,B2>::value ...
template<typename A, typename... B>
struct is_one_of;

// case of empty pack B...
template<typename A>
struct is_one_of<A> : std::false_type {};

// general case: recurse
template<typename A, typename B1, typename... B>
struct is_one_of<A,B1,B...>
: std::integral_constant<bool, std::is_same<A,B1>::value || is_one_of<A,B...>::value> {};

is preferrable, requiring only two specialisations (for an empty pack of Bs and the recursive case). Moreover, is_one_of are good C++ citizens by being derived from std::integral_constant<bool,C>.

Upvotes: 0

aschepler
aschepler

Reputation: 72356

The last specialization IsOneOf<any_of<T1,Ts1...>, any_of<Ts2...>> is not more specialized than IsOneOf<T,any_of<T2,Ts...>> because if Ts2 is an empty list, it can't match the latter. I think you want this, either in place of or in addition to your last specialization:

template <typename T1, typename ... Ts1, typename T2, typename ... Ts2>
struct IsOneOf< any_of<T1,Ts1 ...>, any_of<T2,Ts2 ...> >
{
    const static bool True = IsOneOf<T1, any_of<T2,Ts2...>>::True &&
                             IsOneOf<any_of<Ts1...>, anyOf<T2,Ts2...>>::True;
};

You would also have a problem with IsOneOf<any_of<int,double>, any_of<int,double>> since in that case your IsOneOf<T,T> is ambiguous with this general case. So you can cut that off by specifying the intersection:

template <typename T1, typename ... Ts>
struct IsOneOf< any_of<T1,Ts ...>, any_of<T1,Ts ...> >
{
    const static bool True = true;
};

You will also need base cases dealing with the type any_of<>, if you don't already have them.

Upvotes: 1

Related Questions