6

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...

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
peoro
  • 25,562
  • 20
  • 98
  • 150
  • 2
    What are you **really** trying to do? – Karl Knechtel Dec 06 '10 at 21:22
  • 2
    You should avoid overlapping. That's the answer. The question is the one asked by Karl: What are you really trying to do? – Cătălin Pitiș Dec 06 '10 at 21:25
  • I'm sorry mate, but you are using C++ wrong. Rethink your solution and\or post up the actual problem to get an actual answer. – Squirrelsama Dec 06 '10 at 21:31
  • I'm defining a set of traits for type promotion (similar to how some boost library is doing for floating point promotion): `typename TRAIT< int, float >::type` will be `float` (ie: the promoted type). `typename TRAIT::type` should always be `T` (that's what my first specialization is for, and `typename TRAIT< C, C >::type` should be `C< typename TRAIT::type >`: that's the second specialization. These two sadly overlap (although the result of the two specialization is the same). – peoro Dec 06 '10 at 21:32
  • 4
    All caps identifiers should be reserved for macros. – Puppy Dec 06 '10 at 21:57

3 Answers3

6

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.

Adam Norberg
  • 3,028
  • 17
  • 22
  • I don't know of other ways to do what I need (which I wrote in a comment to the question), and this seems to be the best method to achieve it. – peoro Dec 07 '10 at 09:08
1

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.

bitmask
  • 32,434
  • 14
  • 99
  • 159
0

Try:

template< typename T > struct C { };
template< typename T1, typename T2 = T1> struct TRAIT { };
                              // ^^^^^^^^^
                              // Default Type is T1

int main( )
{
        TRAIT<C<int> >         foo1;
        TRAIT<C<int>, C<int> > foo2;

        foo1 = foo2;
        return 0;
};
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • I am not sure it would work, the OP will likely always pass the two parameters, for it seems like the goal of the traits class to detect if the types are equal. – Matthieu M. Dec 07 '10 at 07:27