41

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<T, A, B, C>::value; // true if T is one of A, B or C

And if T is equal to any one of those types, its static value member will be true, otherwise false. How can I do this?

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
Me myself and I
  • 3,990
  • 1
  • 23
  • 47
  • 3
    Since your intent wasn't clear (two people made the same wrong interpretation) I took the liberty to slightly rephrase your question. – syam Jun 10 '13 at 20:53

6 Answers6

46

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> && ...)> {};

Or as C++20 concepts:

template <typename T, typename... Ts>
concept is_any = std::disjunction_v<std::is_same<T, Ts>...>;

template <typename T, typename... Ts>
concept are_same = std::conjunction_v<std::is_same<T, Ts>...>;
Carsten
  • 11,287
  • 7
  • 39
  • 62
mavam
  • 12,242
  • 10
  • 53
  • 87
  • 4
    this is much cleaner than the checked answer, I feel it should get the checkmark instead, even though the question has a c++11 tag – xception Jun 18 '19 at 12:04
  • The first variation works on C++14, and adding `disjunction` will make it compile even on C++11. – erenon Jul 10 '19 at 18:42
  • I like the disjunction/conjunction solution. Can it be written as a C++20 concept? – Silicomancer Apr 08 '21 at 20:41
34

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
syam
  • 14,701
  • 3
  • 41
  • 65
17

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!

Joald
  • 1,114
  • 10
  • 32
  • The problem with this approach is that it's not really *transitive* friendly. I can't make my own type trait thingy and then link it to `are_all_same` by simply inheriting from it. The same problem applies to alias templates. Therefore, I still prefer to use the approach posted by `mavam`. – 303 Dec 22 '21 at 23:50
  • True, but note that in C++20 you can turn this into a concept and then composition is trivial. – Joald Dec 23 '21 at 01:04
7

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!

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Hmm I see you made the other decision than me (`is_all_same` instead of `is_any`). Truth be told, I hesitated to ask OP for clarification about that. – syam Jun 10 '13 at 20:47
  • Um...This code will only work if *all* the types are the same. I'm looking for if `T` is *either* `A`, `B`, or `C`. – Me myself and I Jun 10 '13 at 20:48
3

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.

enobayram
  • 4,650
  • 23
  • 36
0

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

user1823890
  • 704
  • 8
  • 7