Is it possible to specialize a template definition based on the existence of a nested typedef of a template type parameter?

前端 未结 3 1190
一整个雨季
一整个雨季 2021-02-10 02:57

I have a template, template class wrapper, that I would like to specialize based on the existence of typename T::context_type. If

相关标签:
3条回答
  • 2021-02-10 03:13

    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.

    0 讨论(0)
  • 2021-02-10 03:16

    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 讨论(0)
  • 2021-02-10 03:19

    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.

    0 讨论(0)
提交回复
热议问题