43

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?

1
  • 3
    Since your intent wasn't clear (two people made the same wrong interpretation) I took the liberty to slightly rephrase your question. Commented Jun 10, 2013 at 20:53

7 Answers 7

50

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>...>; 
Sign up to request clarification or add additional context in comments.

5 Comments

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
The first variation works on C++14, and adding disjunction will make it compile even on C++11.
I like the disjunction/conjunction solution. Can it be written as a C++20 concept?
For some reason this is_any doesn't work with variadic templates. So, this doesn't work: template<typename... Types> using EnableIfAllConstCharOrString = std::enable_if_t<std::conjunction_v<is_any<Types, const char*, std::string>>...>>;. I want to check if each argument is either const char* or std::string. So calling the function with MyFunction("", std::string, ""); should be possible.
I'm getting error Error C2210 '_Test': pack expansions cannot be used as arguments to non-packed parameters in alias templates. On the other hand, when I replace that is_any with the template<typename... Types> using EnableIfAllConstCharOrString = std::enable_if_t<std::conjunction_v<std::disjunction<std::is_same<Types, const char*>, std::is_same<Types, std::string>>...>>;, it works as expected and I can call MyFunction("", std::string, "");
35

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 

Comments

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!

2 Comments

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.
True, but note that in C++20 you can turn this into a concept and then composition is trivial.
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!

2 Comments

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

2 Comments

isn't is_same_v c++17? Maybe have to use is_same<>::value with c++14
@xwl You're right, is_same<>{} works in C++14 too, since struct is_same<> has an operator bool overload.
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

Comments

0

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.