39

Is there a standard way for me to select a type at compile-time on an unsigned index in c++11?

For example, something like:

using type_0 = static_switch<0, T, U>;  // yields type T
using type_1 = static_switch<1, T, U>;  // yields type U

If there is a variadic-template version, it would be very useful.

AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
kfmfe04
  • 14,936
  • 14
  • 74
  • 140

4 Answers4

49

This should work:

template<std::size_t N, typename... T>
using static_switch = typename std::tuple_element<N, std::tuple<T...> >::type;

Another method:

template<std::size_t N, typename T, typename... Ts>
struct static_switch {
    using type = typename static_switch<N - 1, Ts...>::type;
};
template<typename T, typename... Ts>
struct static_switch<0, T, Ts...> {
    using type = T;
};
AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
Pubby
  • 51,882
  • 13
  • 139
  • 180
  • 1
    +1 Outstanding answer. I'm always interested in new ways to use variadic templates. Thanks for another one. – WhozCraig Mar 14 '13 at 08:16
  • 1
    @AlexChamberlain not all compilers support it (it they do, it is their latest versions) – BЈовић Mar 14 '13 at 08:25
  • @BЈовић Thanks; I have access to a few different compilers at work, but unfortunately, none of them support C++11 to even start testing this! – Alex Chamberlain Mar 14 '13 at 08:27
  • 8
    Note that something like `std::tuple` yields an invalid instantiation. Someone optimistic will point out that since the only requirement on `std::tuple_element` is that the index be correct, then something like `std::tuple_element<0, std::tuple>::type` should not instantiate `std::tuple` and thus should be well-formed on all implementations. Someone less so optimistic might feel inclined to implement a good old type-list to avoid the consideration altogether. – Luc Danton Mar 14 '13 at 08:30
10

You could probably use a boost::mpl::vector to store your types and use boost::mpl::at<v, n>::type to get a type with from the index.

template<std::size_t N, typename... T>
using static_switch = typename boost::mpl::at<boost::mpl::vector<T...>, N>::type;
AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
Morwenn
  • 21,684
  • 12
  • 93
  • 152
9

How about

 template<size_t N, typename T, typename U>
 struct static_switch {};

 template<typename T, typename U>
 struct static_switch<0, T, U>{typedef T type;};

 template<typename T, typename U>
 struct static_switch<1, T, U>{typedef U type;};

You would use it as follows:

using type_0 = static_switch<0, T, U>::type;  // yields type T
using type_1 = static_switch<1, T, U>::type;  // yields type U

This is more or less implemented for you in std::conditional.

AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
Alex Chamberlain
  • 4,147
  • 2
  • 22
  • 49
  • 11
    Note: `std::conditional` is great if there are only 2 alternatives. Since the OP is talking about an index, there might be more. – Matthieu M. Mar 14 '13 at 08:23
2

With C++17 you can also go about this another way. Instead of calculating the type explicitly you can use constexpr if and do different things (including returning different types) directly:

template<size_t N>
decltype(auto) foo() {
    if constexpr(N % 2 == 0) {
        return std::string("Hello I'm even");
    } else {
        return std::pair(
             std::vector<char>{ 'O', 'd', 'd', ' ', 'v', 'a', 'l', 'u', 'e' },
             [](){ return N; });         
  }
}

foo<0>()           // "Hello I'm even"
foo<21>().second() // 21

You can also use this to get just the type:

using type_0 = decltype(foo<0>());
using type_1 = decltype(foo<1>());
AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
Johan Lundberg
  • 26,184
  • 12
  • 71
  • 97