C++/레퍼런스(Reference)

C++ Reference: std::integer_sequence, std::index_sequence

kim선달 2020. 10. 29. 02:11
Select Language

std::integer_sequence, std::index_sequence

 

Can make integer sequence in compile-time, so it is often used in compile-time indexing.


Defined in header <utility>

template<typename T, T... Ints>
class integer_sequence;

template<std::size_t ...Ints>
using index_sequence = integer_sequence<std::size_t, Ints...>;

 

 

std::index_sequence is specialization of std::integer_sequence, with it's first type as std::size_t(Since C++ standard uses std::size_t for indexing).

So you should be aware of it when computing with signed numbers.

 

 

Below is Helper Templates which can make incrementing integer sequence(0 to N-1)

 

template<typename T, T N>
std::make_integer_sequence<T, N>  // std::integer_sequence that gets 0, 1, 2, ... N-1 as a template parameter

template<std::size_t N>
std::make_index_sequence<N> // std::index_sequence that gets 0, 1, 2, ... N-1 as a template parameter

template<typename ...Args>
std::index_sequence_for<Args...> // std::index_sequence that gets 0, 1, 2, ... sizeof...(Args)-1 as a template parameter

 


Example 1

 

template<typename T, std::size_t... I>
auto make_array(const std::vector<T>& vec, std::index_sequence<I...>){
  return std::array<T, sizeof...(I)>{vec[I]...};
}

template<typename T>
void print_container(const T& arr){
  for(const auto elem : arr)
    std::cout << elem << " ";
  std::cout << std::endl;
}

int main()
{
  std::vector<int> v = {1, 2, 3, 4, 5, -1, -2, -3, -4, -5};
  auto arr = make_array(v, std::make_index_sequence<3>());
  
  print_container(arr);
  print_container(make_array(v, std::index_sequence<1, 3, 5, 7>{}));
}

 

 

Outputs:

1 2 3 
2 4 -1 -3

 

While calling make_array(v, std::make_index_sequence<3>()) 

1. std::make_index_sequence<3>() creates a std::index_sequence<0, 1, 2> object, and pass it to make_array function.

2. the template parameter of std::index_sequence is deduced

3. unpacking vector elements to std::initializer_list

4. construct std::array with it(which size has to be known at compile-time).

5. and returns the std::array

  ※ sizeof...() operator returns the total number of the parameter pack.

 

Same things are happening in make_array(v, std::index_sequence<1, 3, 5, 7>{}).

 

Since you are passing an std::index_sequence object, so you can use either parentheses(calling a default constructor) or a uniform initializer {}.

 


Example 2

 

It can also be used in accessing all members of tuple-like(std::pair, std::tuple) object, which doesn't provide its iterator.

template<typename T>
void print_all(const T& val){
  std::cout << val << std::endl;
}

template<typename T, typename ...Ts>
void print_all(const T& val, const Ts&... vals){
  std::cout << val << " ";
  print_all(vals...);
}

template<typename ...Args, std::size_t... I>
void print_tuple_imp(const std::tuple<Args...>& tup, std::index_sequence<I...>){
  print_all(std::get<I>(tup)...);
}

template<typename ...Args>
void print_tuple(const std::tuple<Args...>& tup){
  print_tuple_imp(tup, std::index_sequence_for<Args...>());
}

int main()
{
  print_tuple(std::make_tuple(1, 2, 3.14));
  
  return 0;
}

 

 

Outputs

1 2 3.14

 

 

Since std::cout doesn't support parameter unpacking in below C++ 20, print_all function is used to print parameter packs.

print_tuple makes std::index_sequence<0, 1, 2> and pass it to print_tuple_imp.

print_tuple_imp is unpacking all tuple element and passing it to print_all.

 


 

 

You can make a custom integer sequence like the below.

 

template <std::size_t ... Is>
constexpr auto zero_sequence (std::index_sequence<Is...> const &)
-> decltype( std::index_sequence<0*Is...>{} ); // multiplying 0 to all template parameters

template <std::size_t N>
using make_zero_sequence
= decltype(zero_sequence(std::make_index_sequence<N>{}));

template<typename T>
void print_container(const T& arr){
  for(const auto elem : arr)
    std::cout << elem << " ";
  std::cout << std::endl;
}

int main()
{
  std::vector<int> v = {1, 2, 3, 4, 5, -1, -2, -3, -4, -5};
  print_container(make_array(v, vmake_zero_sequence<10>()));
  
  return 0;
}

 

Outputs

1 1 1 1 1 1 1 1 1 1 

 

Questions, Feedbacks, and comments are very helpful to me!