Question

How can I specialize a class for a whole base class? So I have

template <bool b>
struct selector
{
};
template <typename T1, typename T2>
struct typeequal : selector<false>
{
};
template <typename T>
struct typeequal<T, T> : selector<true>
{
};

No i would like to create to specialized classes, one for if T1 = T2, and one for if T1 != T2. I tried it with

template <typename T>
class myClass;

template <>
class myClass<selector<true> >
{
    // ...
}
template <>
class myClass<selector<false> >
{
    // ...
}

Then I tried to call it with

myClass<typeequal<int, int> > x;

Which did not work. I think, the problem is, that there are no conversions, when checking which template matches. But how to fix?

Was it helpful?

Solution 2

Oh, ok I see what you mean.

So the problem with the C++ metaprogramming is that you can only pass these kind of things as template argument:

  • Type Parameters.
    • Types
    • Templates (only classes, no functions)
  • Non-type Parameters
    • Pointers
    • References
    • Integral constant expressions

So you have to be able to communicate using only that very limited information that's why some of the templates looks like a spaghetti with many ingredients. You have to use sometimes the ::value or ::type field ( it became some sort of standard ) in order to pass something that cam mimic a value and brings an information that you can use in further evaluation of the template so you can use different tricks and techniques to do it but at the end they will look like a lot of different addons rather than clean and readable implementation. That's why some people invented type_traits which suppose to help simplify some type's operations like the is_base_of - which allows you to check if class A is derived from B but you have to check the ::value again if you want to use as if in some template.

PS. One of the type_trait ability is to check if the type is integral or not is_integral so you can use that if you need to check if pure bool has been used or not. You can also pass this information as test for a bool type within the template argument.

OTHER TIPS

The specialization system does not consider base classes. You can still find a base with SFINAE introspection and partial specialization, though.

template <typename T, typename = void >
class myClass;

template <typename T>
class myClass<T,
    typename std::enable_if<
                  std::is_same<
                        typename T::selector, // If member "selector"
                        selector<true> // is the class, assume it's derived
                  >::value
             > ::type >
{
    // ...
};

template <typename T>
class myClass<T,
    typename std::enable_if< std::is_same< typename T::selector, selector<false> >::value
                             > ::type >
{
    // ...
};

If your goal is to create a specialized versions of myClass - you can try more type wise approach like that:

struct True{};
struct False{};

template < typename T1, typename T2 >
struct typeequal
{
    typedef False value;
};

template < typename T >
struct typeequal< T, T >
{
    typedef True value;
};

template < typename T >
class myClass;


template<>
class myClass< True >
{
    enum{ types_equal = true };
};

template<>
class myClass< False >
{
    enum{ types_equal = false };
};

int main()
{
    myClass< typeequal< int, int >::value > x;
}

If you really want to have inheritance there you can try to use a True and False as a base classes but I don't see a reason for it unless you have something else on your mind.

Hope it will help you :)

edit:

In that case you can do something like that:

#include <stdio.h>

template< bool b >
struct selector {};

template < typename T1, typename T2 >
struct typeequal : selector< false > { };

template < typename T >
struct typeequal< T, T > : selector< true > { };

template < typename T >
class myClass;

template< typename T1, typename T2 >
class myClass< typeequal< T1, T2 > >
{
    public:
        enum{ types_equal = false };
};

template< typename T >
class myClass< typeequal< T, T > >
{
    public:
        enum{ types_equal = true };
};

int main()
{
    myClass< typeequal< int, int > >    x1;
    myClass< typeequal< int, char > >   x2;


    printf( "%d\n", x1.types_equal );
    printf( "%d\n", x2.types_equal );
}

As @Potatowater said, the specialization system does not consider base clases (A type is just a type, there is not covariance/contravariance on the type system).

On the other hand, the easiest way to solve your problem is just use a boolean parameter instead of a type:

template<bool VALUE>
struct boolean_constant
{
    static const bool value = VALUE;
};

typedef boolean_constant<true> true_type;
typedef boolean_constant<false> false_type;


template<typename T , typename U>
struct is_same : public boolean_constant<false> {};

template<typename T>
struct is_same<T,T> : public boolean_constant<true> {};


template<bool CONDITION>
struct myClass
{
    ...
};

template<>
struct myClass<false>
{
    ...
};


 myClass<is_same<int,int>::value> foo;

If you have access to C++11, your goal could be directly achieved using template aliases. Consider:

template<typename RESULT>
struct function
{
    typedef RESULT result;
};

This is a metafunction designed to represent a function, i.e. something which is computed and returns a result via a result alias.

Now you implement your metafunctions using that as base:

template<typename T , typename U>
struct is_same_impl : public function<boolean_constant<false>> {};

template<typename T>
struct is_same_impl<T,T> : public function<boolean_constant<true>> {};

Now the functions you provide to the user are just aliases to the result of the implementation:

template<typename T , typename U>
using is_same = typename is_same_impl<T,U>::result;

Using that you could specialize your class in the way you have been trying:

template<typename T>
struct myClass;

template<>
struct myClass<true_type>
{
    ...
};

template<>
struct myClass<false_type>
{
    ...
};


myClass<is_same<int,int>> foo;

As you suspected, templates do not implicitly deal with inheritance in the specialization mechanism. However, you can explicitly manage it. Here's an example:

#include <iostream>
#include <utility>

template <bool b>
struct selector
{
};
template <typename T1, typename T2>
struct typeequal : selector<false>
{
};
template <typename T>
struct typeequal<T, T> : selector<true>
{
};

template <bool b>
selector<b> as_selector(selector<b> *);

template <typename T>
struct myClass : myClass<decltype(as_selector((T*)nullptr))> {
};

template <>
struct myClass<selector<true> >
{
    bool value() { return true; }
};

template <>
struct myClass<selector<false> >
{
    bool value() { return false; }
};

int main()
{
  myClass<typeequal<int,int> > x;
  myClass<typeequal<int,float> > y;
  std::cout << x.value() << "\n";
  std::cout << y.value() << "\n";
}

This outputs 1 and 0.

The trick here is to explicitly convert the template parameter T to the appropriate selector<b> base class using function templates, which do handle inheritance. We first define a function which can take a pointer to a selector<b> (or anything derived from it), and have it return the matching selector type:

template <bool b>
selector<b> as_selector(selector<b> *);

Next, we use decltype to give us back the type the function would return; just passing a null pointer to T, and we derive our general myClass from it:

template <typename T>
struct myClass : myClass<decltype(as_selector((T*)nullptr))> {
};

Now since myClass<type_equal<A,B>> derives from selector<b>, we can specialize myClass<selector<b>> to define the functionality in each case.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top