How can I avoid writing `::value` and `::type` when using `std::enable_if`? [cppx]

后端 未结 3 2052
死守一世寂寞
死守一世寂寞 2021-02-01 07:39

Note: This is a question-with-answer in order to document a technique that others might find useful, and in order to perhaps become aware of others’

3条回答
  •  傲寒
    傲寒 (楼主)
    2021-02-01 08:14

    The facility for a comfortable-with-C++11-using compiler is simply …

    namespace cppx {
        using std::enable_if;
    
        template< class Condition_type, class Result_type = void >
        using If_ = typename enable_if::type;
    
    }  // namespace cppx
    

    Support for a more using-challenged compiler such as Visual C++ 12.0 and earlier (it understands the basic use of using but gets more and more unreliable the more the usage context has things like enable_if) is a bit more involved, building on a C++03-style solution like …

    namespace cppx {
        using std::enable_if;
    
        template<
            class Condition_type,
            class Result_type = void,
            class enabled = typename enable_if::type 
            >
        struct If_T_
        {
            typedef Result_type     T;
            typedef Result_type     type;
        };
    
    }  // namespace cppx
    

    This basically only provides a more readable name and dispenses with the ::value in a condition. In order to also dispense with typename and ::type I use a macro. But since the expression will generally be a template expression there may be commas that the preprocessor will interpret as argument separators, so that the preprocessor may see multiple arguments.

    The solution I use for that (the time of C++03 is over for me) is to use a C99/C++11 variadic macro, …

    #define CPPX_IF_( ... ) \
        typename cppx::If_T_< __VA_ARGS__ >::T
    

    A corresponding macro could be defined for use of this functionality without the typename.


    Complete listing, file rfc/cppx/utility/If_.h:

    #pragma once
    // Copyright (c) 2013 Alf P. Steinbach
    
    #include       // std::enable_if
    
    #define CPPX_IF_( ... ) \
        typename cppx::If_T_< __VA_ARGS__ >::T
    
    namespace cppx {
        using std::enable_if;
    
        template< class Condition_type, class Result_type = void >
        using If_ = typename enable_if::type;
    
        template<
            class Condition_type,
            class Result_type = void,
            class enabled = typename enable_if::type 
            >
        struct If_T_
        {
            typedef Result_type     T;
            typedef Result_type     type;
        };
    
    }  // namespace cppx
    

    Also, for completeness, Is_a_ is defined simply as …

    template< class Base, class Derived_or_eq >
    using Is_a_ = std::is_base_of;
    

    which is a use of using that Visual C++ 12.0 does understand.


    To be able to use compound conditions without writing ::value everywhere, the following definitions come in handy. Essentially these are boolean operators that operate on types. It is perhaps worth noting especially the general exclusive OR operator, which is not implemented in terms of binary XOR (e.g. !=): that would have yielded an operator that checked for the odd number of true values, which is of little practical utility except for the special case of exactly two arguments.

    namespace cppx {
        using std::integral_constant;
    
        template< bool c >
        using Bool_ = integral_constant;
    
        using False = Bool_;     // std::false_type;
        using True  = Bool_;      // std::true_type;
    
        template< bool v, class First, class... Rest >
        struct Count_
        {
            enum{ value = Count_::value + Count_::value };
        };
    
        template< bool v, class X >
        struct Count_
        {
            enum{ value = int(!!X::value == v) };
        };
    
        template< class X >
        using Not_ = Bool_::value == 0>;                   // NOT
    
        template< class... Args >
        using All_ = Bool_::value == 0>;            // AND
    
        template< class... Args >
        using Some_ = Bool_::value != 0>;     // General inclusive OR.
    
        template< class... Args >
        using Either_ = Bool_::value == 1>;   // General exclusive OR.
    }  // namespace cppx
    

    Disclaimer: none of the code has been extensively tested, and C++ compiler quirks in the area of template meta-programming are common.

提交回复
热议问题