Matching a type T to a list of case statements I have a compile-time switch_ template that matches a given type T to a list of case_ statements (of which default_ is a special case, no pun intended). A case&lt;Key, Value is two-parameter class for which its Key is matched against the class T using a match&lt;T, Key&gt; variable template, which is a modified std::is_same_v.

#include &lt;cstdint&gt;      // uint8_t, uint16_t, uint32_t, uint64_t
#include &lt;type_traits&gt;  // conditional_t, is_same_v, true_type, false_type

namespace meta {

template&lt;auto N&gt;
using int_c = std::integral_constant&lt;decltype(N), N&gt;;

struct true_;
struct false_;

template&lt;class Key, class Value&gt;
struct case_;

template&lt;class Value&gt;
using default_ = case_&lt;true_, Value&gt;;

template&lt;class T, class Key&gt;
constexpr auto match = std::is_same_v&lt;T, Key&gt;;

template&lt;class T&gt; constexpr auto match&lt;T, true_&gt; = true;
template&lt;class T&gt; constexpr auto match&lt;true_, T&gt; = true;

template&lt;class T&gt; constexpr auto match&lt;T, false_&gt; = false;
template&lt;class T&gt; constexpr auto match&lt;false_, T&gt; = false;

template&lt;class T, class... Cases&gt;
struct switch_;

template&lt;class T, class... Cases&gt;
using switch_t = typename switch_&lt;T, Cases...&gt;::type;

template&lt;class T, class Key, class Value&gt;
struct switch_&lt;T, case_&lt;Key, Value&gt;&gt;
:
        std::conditional&lt;match&lt;T, Key&gt;, Value, false_&gt;
{};

template&lt;class T, class Key, class Value, class _, class Else&gt;
struct switch_&lt;T, case_&lt;Key, Value&gt;, case_&lt;_, Else&gt;&gt;
:
        std::conditional&lt;match&lt;T, Key&gt;, Value, Else&gt;
{};

template&lt;class T, class Key, class Value, class Head, class... Tails&gt;
struct switch_&lt;T, case_&lt;Key, Value&gt;, Head, Tails...&gt;
:
        std::conditional&lt;match&lt;T, Key&gt;, Value, switch_t&lt;T, Head, Tails...&gt;&gt;
        //                                     ^^^^^^^^ &lt;= can this recursion be avoided?
{};

}   // namespace meta

using namespace meta;

template&lt;auto N&gt;
using unsigned_ = switch_t&lt;         // will return associated type of first match, case_ has "break" semantics
    int_c&lt;N&gt;,
    case_&lt;int_c&lt;8&gt;, uint8_t&gt;,
    case_&lt;int_c&lt;16&gt;, uint16_t&gt;,
    case_&lt;int_c&lt;32&gt;, uint32_t&gt;,
    case_&lt;int_c&lt;64&gt;, uint64_t&gt;,
    default_&lt;unsigned&gt;              // will always match, so put this last!
&gt;;

int main()
{
    static_assert(std::is_same_v&lt;unsigned_&lt;  8&gt;, uint8_t&gt;);
    static_assert(std::is_same_v&lt;unsigned_&lt; 16&gt;, uint16_t&gt;);
    static_assert(std::is_same_v&lt;unsigned_&lt; 32&gt;, uint32_t&gt;);
    static_assert(std::is_same_v&lt;unsigned_&lt; 64&gt;, uint64_t&gt;);
    static_assert(std::is_same_v&lt;unsigned_&lt;128&gt;, unsigned&gt;);
}


Live Example using gcc 7.1 in C++17 mode.

Question: Please comment on the clarity of the code. In particular, I am curious if the compile-time recursion over the variadic parameter pack can somehow be avoided by mapping it to tuple or integer_sequence land where there are efficient compiler-intrinsics that pay off for long lists.

UPDATE: See this link for the updated code with changes that reflects the accepted answer.
I don't think returning the artificial false_ in case of no match is a good idea. Just return nothing, and you won't need false_ at all. Replace


: std::conditional&lt;match&lt;T, Key&gt;, Value, false_&gt;



with

: std::enable_if&lt;match&lt;T, Key&gt;, Value&gt;


It's interesting that while you support an explicit default-case, the last case is always treated as the default case anyway, if it isn't the only case. Oops?


template&lt;class T, class Key, class Value, class _, class Else&gt;
struct switch_&lt;T, case_&lt;Key, Value&gt;, case_&lt;_, Else&gt;&gt;
:
        std::conditional&lt;match&lt;T, Key&gt;, Value, Else&gt;
{};



By the way, if you restrict the default-case to being the last case, which you should consider doing anyway, you can replace match with std::is_same and some changes to switch_, making default_ its own class-template. Might be worth it.

What I failed to find is any way to avoid the recursion, sorry. I hope you can make good use of my feedback anyway.

Modified code:

namespace meta {

// Only for the example-program:
template&lt;auto N&gt;
using int_c = std::integral_constant&lt;decltype(N), N&gt;;

template&lt;class Key, class Value&gt;
struct case_;

template&lt;class Value&gt;
struct default_;

template&lt;class T, class... Cases&gt;
struct switch_
{
    static_assert(!sizeof...(Cases), "this meta::switch_ is malformed");
}

template&lt;class T, class Value&gt;
struct switch_&lt;T, default_&lt;Value&gt;&gt;
: std::enable_if&lt;true, Value&gt;
{}

template&lt;class T, class Key, class Value, class... Tail&gt;
struct switch_&lt;T, case_&lt;Key, Value&gt;, Tail...&gt;
: std::conditional_t&lt;std::is_same&lt;T, Key&gt;(),
    std::enable_if&lt;true, Value&gt;,
    switch_&lt;T, Tail...&gt;&gt;
{}

template&lt;class T, class... Cases&gt;
using switch_t = typename switch_&lt;T, Cases...&gt;::type;

}   // namespace meta