Is it possible to specialize a template definition based on the existence of a nested typedef of a template type parameter?
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_)
{开发者_C百科
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:
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.
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.
精彩评论