這篇雖然是在講 C++14 的「std::integer_sequence」(cppreference),不過實際上應該算是之前《C++11 的「…」:Parameter Pack》一文的延伸。
std::integer_sequence 是一個用來建立編譯階段(compile-time )的整數序列(不是陣列)的 template class,定義如下:
template< class T, T... Ints > class integer_sequence;
而他是被定義在標準函式庫的 <utility> 這個標頭檔裡面,所以要使用的話,就需要引入這個檔案。
他最基本的使用方法大致上如下:
std::integer_sequence<int, 1, 3, 5, 7>()
這樣就代表了一個型別是 int 的序列、裡面有 1, 3, 5, 7 四個數值。
不過如果要使用這個序列,並不能直接用,而是要使用 Parameter Pack 的 Pack Expansion 的方法,才能存取裡面的值。
比如說想要把上面的 std::integer_sequence 轉換成 std::array 的形式的話,那可以寫成下面的樣子:
#include <utility> #include <array> template<class T, T... I> std::array<T, sizeof...(I)> make_array(std::integer_sequence<T, I...>) { return{ I... }; } int main() { std::array<int,4> x
= make_array( std::integer_sequence<int, 1, 3, 5, 7>() ); return 0; }
透過 make_array() 這個函式,就可以把 std::integer_sequence 的數列轉換成 std::array<int,4> 了。
而如果是要產生連續遞增的序列的話,則可以直接使用 std::make_integer_sequence() 這個函式,來快速地產生 0, 1,…, N-1 這樣的序列。
例如下面這樣的程式:
std::array<int,7> x = make_array(std::make_integer_sequence<int, 7>());
執行後,x 就會是 { 0, 1, 2, 3, 4, 5, 6 } 這樣的陣列。
而如果要更簡化的話,他也還有提供一個特化成 size_t 的版本、index_sequence,其定義如下:
template<std::size_t... Ints> using index_sequence = std::integer_sequence<std::size_t, Ints...>;
而他也有對應的 make_index_sequence() 可以用。下面就是簡單的用法:
std::array<size_t,7> x = make_array( std::make_index_sequence<7>() );
和前面使用 make_integer_sequence() 的例子相比,唯一的差別就是 x 裡面每一項的型別都是 size_t 了。
而這東西到底有什麼用呢?
一個例子,或許可以考慮是容器類型的轉換,例如下面就是把 std::array<> 的資料,轉換成 std::tuple<>:
#include <utility> #include <array> #include <tuple> template<typename Array, std::size_t... I> auto a2t_impl(const Array& a, std::index_sequence<I...>) { return std::make_tuple(a[I]...); } template<typename T, std::size_t N, typename Indices = std::make_index_sequence<N>> auto a2t(const std::array<T, N>& a) { return a2t_impl(a, Indices()); } int main() { std::array<int, 5> aData = { 1,2,3,4,5 }; auto aDatatTuple = a2t(aData); return 0; }
透過上面的函式,就可以把 std::array<int,5> 的 aData,轉換成 std::tuple<int,int,int,int,int> 了~
這邊之所以把函式拆成 a2t() 和 a2t_impl() 兩部分的原因,主要是因為 std::integer_sequence 基本上還是需要當作函式的引數傳入(a2t_impl() 的部分);而如果要考慮使用上的便利性的話,還是應該寫在函式裡處理掉(a2t() 的部分)。
如此一來,在外部呼叫的時候,就只需要呼叫 a2t()、而不用管 std::integer_sequence 的東西了~
而另一種應用,則還可以用來把 std::tuple 展開、當作函式的引數傳進去;例如下面的函式,就是一種可能性的實作:
template<typename FUNC, typename PARA, std::size_t... I> auto call_impl(FUNC& f, const PARA& a, std::index_sequence<I...>) { return f(std::get<I>(a)...); } template<typename FUNC, typename PARA> auto call(FUNC& f, const PARA& a) { static constexpr auto t_count = std::tuple_size<PARA>::value; return call_impl(f, a, std::make_index_sequence<t_count>()); }
比如說想要把 std::tuple 當作引數、呼叫
bool test_func( int a, float b )
這樣的函式的話,可以寫成:
std::tuple<int, float> p = std::make_tuple(1, 1.0f); bool res = call(test_func, p);
而如果搭配之前《C++11 的「…」:Parameter Pack》中提到的 make_tuple_of_params()、自動提取出函式的引數成 std::tuple 的形式的話,應該會有一些特別的玩法吧?
這篇大概就這樣了。
老實說,Heresy 也還不知道這東西到底能拿來幹嘛?就姑且先記錄一下,搞不好以後會有用了。