I have a template, template
, that I would like to specialize based on the existence of typename T::context_type
. If
That's possible, and there are many ways to implement this. All of them should go back on some trait class has_type
so that has_type<T>::value
is true if the member typedef exists, and false otherwise. Let's assume we have this trait class already. Then here's one solution, using C++11 template aliases:
template <typename T, bool> class FooImpl
{
// implement general case
};
template <typename T> class FooImpl<T, true>
{
// implement specific case
};
template <typename T> using Foo = FooImpl<T, has_type<T>::value>; // C++11 only
Now to make the typetrait:
template<typename T>
struct has_type
{
private:
typedef char yes;
typedef struct { char array[2]; } no;
template<typename C> static yes test(typename C::context_type*);
template<typename C> static no test(...);
public:
static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};
If you don't have C++11, or if you don't want to rewrite the entire class, you can make the distinction more fine-grained, e.g. by using std::enable_if
, std::conditional
, etc. Post a comment if you want some specific examples.
Using @K-ballo's answer, I wrote the following:
namespace detail {
BOOST_MPL_HAS_XXX_TRAIT_DEF(context_type)
}
template <typename T, typename Enable = void>
class wrapper
{
public:
wrapper() {
std::cout << "T::context_type does not exist." << std::endl;
}
};
template <typename T>
class wrapper<T, typename boost::enable_if<detail::has_context_type<T> >::type>
{
public:
typedef typename T::context_type context_type;
private:
context_type ctx;
public:
wrapper(context_type ctx_)
: ctx(ctx_)
{
std::cout << "T::context_type exists." << std::endl;
}
};
Now, the sample code compiles and outputs:
T::context_type exists. T::context_type does not exist.
Yes, it is possible. I have implemented such behavior in the past by using some metaprogramming tricks. The basic build blocks are:
BOOST_MPL_HAS_XXX_TRAIT_DEF
, to define a metafunction predicate that will evaluate to a true type if the argument is of class type and has a nested type with a given name (context_type in your case).
http://www.boost.org/doc/libs/1_47_0/libs/mpl/doc/refmanual/has-xxx-trait-def.html
Boost.EnableIf
, to define the specializations based on the previously defined trait.
http://www.boost.org/libs/utility/enable_if.html # See 3.1 Enabling template class specializations
Note that you may be able to get that behavior working directly with SFINAE, something like this may work:
template< typename T, typename Context = void >
class wrapper { ... }; // Base definition
template< typename T >
class wrapper< T, typename voif_mfn< typename T::context_type >::type > { ... }; // Specialization
However, I like the expressiveness of the solution based on traits and enable if.