Me myself and I
Me myself and I

Reputation: 4060

How to make a variadic is_same?

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?

Upvotes: 42

Views: 9830

Answers (7)

BCS
BCS

Reputation: 78545

If you already have the set of types as a parameter pack you can use a fold expression (added in C++17):

template<class... Ts> 

...  // A few lines later

if constexpr ((... || std::is_same_v<T, Ts>)) {
  // ...
}

NOTE: the doubled (()) are required because the inner () are part of the fold expression.

Upvotes: 0

mavam
mavam

Reputation: 12552

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>...>;

Upvotes: 49

user1823890
user1823890

Reputation: 744

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

Upvotes: 0

enobayram
enobayram

Reputation: 4698

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.

Upvotes: 3

Joald
Joald

Reputation: 1134

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!

Upvotes: 17

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275385

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!

Upvotes: 7

syam
syam

Reputation: 15069

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

Upvotes: 35

Related Questions