Is it possible to specialize a template definition based on the existence of a nested typedef 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 , then the constructors and overloads of the instance assignment operator wrapper<T> must accept the required parameter typename T::context_type . In addition, wrapper<T> objects will store the "context" in the element data. If typename T::context_type does not exist, then the constructors and overloads of the wrapper<T> assignment operator will have one smaller parameter and there will be no additional data element.

Is it 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; } 
+4
source share
3 answers

Yes it is possible. I have implemented this behavior in the past using some metaprogramming tricks. The main building blocks are:

BOOST_MPL_HAS_XXX_TRAIT_DEF to define a metafunction predicate that will be evaluated as a true type if the argument has a class type and has a nested type with the 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 specializations based on a previously defined attribute.

http://www.boost.org/libs/utility/enable_if.html # See 3.1 Enabling Template Class Specialization


Note that you can make this behavior work directly with SFINAE, something like this might 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 trait-based solution, and allow, if.

+3
source

It is possible, and there are many ways to implement this. All of them must return to some has_type attribute class so that has_type<T>::value true if the typedef member exists, and false otherwise. Suppose we already have this feature class. Then here is 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 do a 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 whole class, you can make the difference finer, for example. using std::enable_if , std::conditional etc. Post a comment if you need some specific examples.

+4
source

Using @ K-ballo 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 is compiled and output:

  T :: context_type exists.
 T :: context_type does not exist.
+1
source

All Articles