I have a template, template <typename T> class wrapper
, that I would like to specialize based on the existence of typename T::context_type
. If typename T::context_type
is declared, then the constructors and assignment operator overloads of the wrapper<T>
instantiation should accept a mandatory typename T::context_type
parameter. Additionally, wrapper<T>
objects would store "context" in the member data. If typename T::context_type
does not exist, then the constructors and assignment operator overloads of wrapper<T>
would take one less parameter and there would be no additional data member.
Is this possible? Can I get the following code to compile without changing the definitions of config1
, config2
, and main()
?
#include <iostream>
template <typename T, bool context_type_defined = true>
class wrapper
{
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;
}
};
template <typename T>
class wrapper<T, false>
{
public:
wrapper() {
std::cout << "T::context_type does not exist." << std::endl;
}
};
class config1 {
public:
typedef int context_type;
};
class config2 {
public:
};
int main()
{
wrapper<config1> w1(0);
wrapper<config2> w2;
}
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:
However, I like the expressiveness of the solution based on traits and enable 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 thathas_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:Now to make the typetrait:
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:
Now, the sample code compiles and outputs: