开发者

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.
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜