Template specialization to use default type if class member typedef does not exist

后端 未结 3 1192
误落风尘
误落风尘 2020-12-14 03:44

I\'m trying to write code that uses a member typedef of a template argument, but want to supply a default type if the template argument does not have that typedef. A simpli

相关标签:
3条回答
  • 2020-12-14 04:00

    First step: stop using "Type" and use the mpl standard "type".

    
    BOOST_MPL_HAS_XXX_DEF(Type)
    
    template < typename T >
    struct get_type { typedef typename T::Type type; };
    
    template < typename T >
    struct calculate_type : boost::mpl::if_
    <
      has_Type<T>
    , get_type<T>
    , boost::mpl::identity<default_type>
    >::type {}
    
    typedef calculate_type<A>::type whatever;
    
    

    If you used "type" instead of "Type" in your metafunctions you wouldn't require the fetcher "get_type" to convert it and could just return T in that case.

    0 讨论(0)
  • 2020-12-14 04:02

    To answer your addition - your specialization argument passes the member typedef and expects it to yield void as type. There is nothing magic about this - it just uses a default argument. Let's see how it works. If you say Get_Type<Foo>::type, the compiler uses the default argument of Enable, which is void, and the type name becomes Get_Type<Foo, void>::type. Now, the compiler checks whether any partial specialization matches.

    Your partial specialization's argument list <T, typename T::Type> is deduced from the original argument list <Foo, void>. This will deduce T to Foo and afterwards substitutes that Foo into the second argument of the specialization, yielding a final result of <Foo, NonDefaultType> for your partial specialization. That doesn't, however, match the original argument list <Foo, void> at all!

    You need a way to yield the void type, as in the following:

    template<typename T>
    struct tovoid { typedef void type; };
    
    template<typename T, typename Enable = void> struct Get_Type { 
        typedef DefaultType Type; 
    };
    template<typename T> 
    struct Get_Type< T, typename tovoid<typename T::Type>::type > {
        typedef typename T::Type  Type; 
    };
    

    Now this will work like you expect. Using MPL, you can use always instead of tovoid

    typename apply< always<void>, typename T::type >::type
    
    0 讨论(0)
  • 2020-12-14 04:16

    You can do that by utilizing SFINAE:

    template<class T> struct has_type {
        template<class U> static char (&test(typename U::Type const*))[1];
        template<class U> static char (&test(...))[2];
        static const bool value = (sizeof(test<T>(0)) == 1);
    };
    
    template<class T, bool has = has_type<T>::value> struct Get_Type {
        typedef DefaultType Type;
    };
    
    template<class T> struct Get_Type<T, true> { 
        typedef typename T::Type Type;
    };
    
    0 讨论(0)
提交回复
热议问题