Reputation: 1731
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?
Upvotes: 0
Views: 260
Reputation: 64308
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.
Upvotes: 0
Reputation: 14174
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;
Upvotes: 0
Reputation: 137870
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 >
{
// ...
};
Upvotes: 1
Reputation: 161
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:
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.
Upvotes: 0
Reputation: 161
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 );
}
Upvotes: 0