Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ambiguous partial template specialization

I've got a trait class which I need to specialize (and partial-specialize) many times.

Some partial specializations overlap:

template< typename T > struct C { };
template< typename T1, typename T2 > struct TRAIT { };

template< typename T > struct TRAIT< T, T > { };
template< typename T1, typename T2 > struct TRAIT< C<T1>, C<T2> > { };

int main( ) {
    // ERROR! could be both TRAIT<T,T>   [with T = C<int>]
    //                  and TRAIT<T1,T2> [with T1 = T2 = int]
    TRAIT< C<int>, C<int> > foo;
    return 0;
};

How am I supposed to get the same result with a working code?

I went crazy with enable_if and is_same, I'm not even sure anymore it's the right way...

like image 249
peoro Avatar asked Dec 06 '10 21:12

peoro


2 Answers

Your best bet for this, if you can't avoid overlapping specialization, is to clarify all your overlaps. You'll need to write another specialization for

template< typename T> struct TRAIT< C<T>, C<T> > { };

...but, as everybody else said in the comments, it's best to avoid overlapping if at all possible. As others called out, the problem may not be overlapping specializations, it may be that this isn't the best approach to solve your problem.

like image 92
Adam Norberg Avatar answered Sep 24 '22 01:09

Adam Norberg


The trick is to create a helper type that tells you if something is a C<Foo> or not:

template <typename T> struct C {};

template <typename T> struct is_C {
  static bool const value = false;
};
template <typename T> struct is_C<C<T>> {
  static bool const value = true;
};

You were on the right track with enable_if but is_same seems to be a dead end:

#include <type_traits>

template <typename T1, typename T2, typename Enabled = void> struct Trait {
  static char const test = 'D'; // default
};

template <typename T> struct Trait<
    T,
    T,
    typename std::enable_if<!is_C<T>::value >::type
> {
  static char const test = 'N'; // non-C
};

template <typename T1, typename T2> struct Trait<
    T1,
    T2,
    typename std::enable_if< is_C<T1>::value && is_C<T2>::value >::type
> {
  static char const test = 'C'; // some C
};

Which you can test easily now:

#include <iostream>

int main() {
  Trait< C<int>, C<float> > foo1a;
  Trait< C<int>, C<int> > foo1b;
  Trait< int, int > foo2;
  Trait< int, float > foo3;
  std::cout << "Trait<C<int>,C<float>> : " << foo1a.test << std::endl; // prints 'C'
  std::cout << "Trait<C<int>,C<int>> : " << foo1b.test << std::endl; // prints 'C'
  std::cout << "Trait<int,int> : " << foo2.test << std::endl; // prints 'N'
  std::cout << "Trait<int,float> : " << foo3.test << std::endl; // prints 'D'
  return 0;
}

You can easily change that if you want your specialisation to work only with one of the parameters to be a C or whatever you like.

like image 42
bitmask Avatar answered Sep 20 '22 01:09

bitmask