If C ++ 11 is an option for you, here is some code illustrating a possible solution using std::enable_if and std::is_same :
#include <iostream> #include <type_traits> struct SdkVariant { }; typedef int type1; typedef float type2; template <typename T, typename Enable=void> class variant_cast { public: /* Default implementation of the converter. This is undefined, but you can define it to throw an exception instead. */ T operator()(const SdkVariant &v); }; /* Conversion for type1. */ template <typename T> class variant_cast<T,typename std::enable_if<std::is_same<T,type1>::value>::type> { public: type1 operator()(const SdkVariant &v) { return type1 { 0 }; } }; /* Conversion for type2, IF type2 != type1. Otherwise this specialization will never be used. */ template <typename T> class variant_cast<T,typename std::enable_if< std::is_same<T,type2>::value && !std::is_same<type1,type2>::value>::type> { public: type2 operator()(const SdkVariant &v) { return type2 { 1 }; } }; int main() { variant_cast<type1> vc1; variant_cast<type2> vc2; std::cout << vc1({}) << std::endl; std::cout << vc2({}) << std::endl; return 0; }
A few notes:
- Instead of the different types that you define with this library, I defined only
type1 and type2 - I defined an empty
SdkVariant structure as dummy - Since this dummy is empty, my conversion does not really convert anything. It simply prints a constant (value 0) when converting to
type1 and a constant (value 1) when converting to type2 (if type2 actually different from type1 ). To check if it does what you need, you can replace the definition of type2 with
typedef int type2;
therefore, it is identical to the definition for type1 . It will still compile, and there will be no error associated with any kind of double definition.
- I tested this with GCC 4.7.0 and
--std=c++11 .
A note on using std::enable_if and partial or explicit custom patterns
The converter for type1 declared as
template <typename T> variant_cast<T,typename std::enable_if<std::is_same<T,type1>::value>::type>
which means that it is defined for any type T that matches type1 . Instead, we could use explicit specialization
template <> variant_cast<type1>
which is much simpler, but also works .
The only reason I did not do this is in the case of type2 , it will not work , because for type2 we have to check if this matches type1 , i.e. we should use std::enable_if :
template <> class variant_cast<type2, typename std::enable_if<!std::is_same<type1,type2>::value>::type>
Unfortunately, you cannot use std::enable_if in explicit specialization, because explicit specialization is not a template - it is a real data type, and the compiler must process it. If type1 and type2 identical, this is:
typename std::enable_if<!std::is_same<type1,type2>::value>::type
does not exist, as std::enable_if works. Thus, compilation fails because it cannot create an instance of this data type.
By defining a converter for any type T that matches type2 , we avoid an explicit instance for type2 , so we do not force the compiler to process it. It will only handle template specialization for type2 if std::enable_if<...>::type exists. Otherwise, he will simply ignore what exactly we want.
Again, in the case of type1 (and for any further type3 , type4 , etc.), explicit instantiation will work.
I find it worthwhile to point out that defining template specialization for any type T , similar to some type type , is a trick that is usually applicable when you cannot use explicit specialization for formal reasons, so you use partial specialization, but you really want to bind it only to this type. For example, a member template cannot be explicitly instantiated unless its wrapper template is explicitly created. Using a combination of std::enable_if and std::is_same probably also helps there.