How to make a variadic is_same?

后端 未结 6 779
刺人心
刺人心 2020-12-05 02:07

How can I make a class template that returns whether any of its variadic types are equal to the first type. I want to be able to do this:

is_same

        
相关标签:
6条回答
  • 2020-12-05 02:49

    Use template recursion:

    template<typename T, typename... Rest>
    struct is_any : std::false_type {};
    
    template<typename T, typename First>
    struct is_any<T, First> : std::is_same<T, First> {};
    
    template<typename T, typename First, typename... Rest>
    struct is_any<T, First, Rest...>
        : std::integral_constant<bool, std::is_same<T, First>::value || is_any<T, Rest...>::value>
    {};
    
    static_assert(is_any<int, char, double, int>::value, "error 1");   // OK
    static_assert(is_any<int, char, double, short>::value, "error 2"); // error
    
    0 讨论(0)
  • 2020-12-05 02:57

    The most generic version that works :

    • since C++11

    • without dependencies (no #include <type_traits> needed)

    • only one name is_same works for n-types (no is_same_all needed)

    template <typename First, typename Second, typename ... Next>
    struct is_same {
    
        template <typename A, typename B>
        struct is_same_min {
            enum { value = false };
        };
    
        template <typename A>
        struct is_same_min<A,A> {
            enum { value = true };
        };
    
        template <typename X, typename Y>
        constexpr static bool check() {
            return is_same_min<X,Y>::value;
        };
    
        template <typename X, typename Y, typename Z, typename ... K>
        constexpr static bool check() {
            return is_same_min<X,Y>::value and check<Y, Z, K...>();
        };
    
        enum { value = check<First, Second, Next...>() };
    };
    
    

    Just use is_same<T1,T2,T3...>::value

    0 讨论(0)
  • 2020-12-05 03:01

    Using the relaxed C++14 constexpr functions, these kinds of things are much easier to code, and probably much faster to compile as well, so you could write:

    template <class T, class ... Candidates>
    constexpr bool is_all_same() {
        bool pairs[] = {std::is_same<T,Candidates>::value...};
        for(bool p: pairs) if(!p) return false;
        return true;
    }
    
    template <class T, class ... Candidates>
    constexpr bool is_any_same() {
        bool pairs[] = {std::is_same<T,Candidates>::value...};
        for(bool p: pairs) if(p) return true;
        return false;
    }
    

    This is enabled by the fact that in C++14 constexpr functions can have for loops.

    0 讨论(0)
  • 2020-12-05 03:02

    Nice and concise with C++17:

    template <class T, class... Ts>
    struct is_any : std::disjunction<std::is_same<T, Ts>...> {};
    

    And the dual:

    template <class T, class... Ts>
    struct are_same : std::conjunction<std::is_same<T, Ts>...> {};
    

    A variation that uses fold expressions:

    template <class T, class... Ts>
    struct is_any : std::bool_constant<(std::is_same_v<T, Ts> || ...)> {};
    
    template <class T, class... Ts>
    struct are_same : std::bool_constant<(std::is_same_v<T, Ts> && ...)> {};
    
    0 讨论(0)
  • 2020-12-05 03:08

    In C++17 you have an even nicer solution, using template variables and fold expressions:

    template<class T, class... Rest>
    inline constexpr bool are_all_same = (std::is_same_v<T, Rest> && ...);
    

    And the usage is also simpler than all other examples:

    are_all_same<T, A, B, C>
    

    No ::value, no parentheses!

    0 讨论(0)
  • 2020-12-05 03:10

    Something like this. First, a small metaprogramming library, because it adds like 2 lines to do it generically:

    template<template<typename,typename>class checker, typename... Ts>
    struct is_any_to_first : std::false_type {};
    
    template<template<typename,typename>class checker, typename T0, typename T1, typename... Ts>
    struct is_any_to_first<checker, T0, T1, Ts...> :
      std::integral_constant< bool, checker<T0, T1>::value || is_any_to_first<checker, T0, Ts...>::value>
    {};
    

    Then a 2 line implementation of is_any_same_to_first:

    template<typename... Ts>
    using is_any_same_to_first = is_any_to_first< std::is_same, Ts... >;
    

    And for completeness, the original is_all, which may also prove useful:

    template<template<typename,typename>class checker, typename... Ts>
    struct is_all : std::true_type {};
    
    template<template<typename,typename>class checker, typename T0, typename T1, typename... Ts>
    struct is_all<checker, T0, T1, Ts...> :
      std::integral_constant< bool, checker<T0, T1>::value && is_all<checker, T0, Ts...>::value>
    {};
    
    template<typename... Ts>
    using is_all_same = is_all< std::is_same, Ts... >;
    

    Live example of the is_all_same.

    Note that calling is_any_same_to_first anything less explicit is asking for trouble. 2/3 people who tried to answer this question, including me, assumed that is_same<A,B,C> is true iff all three are the same type!

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