Skip to content

Instantly share code, notes, and snippets.

@loliGothicK
Last active May 15, 2021 04:21
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save loliGothicK/fd40e066bd666f5a8a73d38391c6692a to your computer and use it in GitHub Desktop.
Save loliGothicK/fd40e066bd666f5a8a73d38391c6692a to your computer and use it in GitHub Desktop.
C++のパラメータパック基礎&パック展開テクニック ref: https://qiita.com/_EnumHack/items/677363eec054d70b298d
template < typename ...Args >
void func( Args ...args );
template <typename... Args>
auto my_make_tuple(Args ...args)
-> std::tuple<Args...> // テンプレートパラメータパックの展開
{
return { args... }; // 関数パラメータパックの展開
}
// テンプレートパラメータパックのパラメータすべてに
// const& を付加して
// パラメータパックArgsの全てのパラメータを、
// constな左辺値参照として受け取る
template < typename ...Args >
void f( const Args&... args ) {}
template <typename ...Args>
void f( Args&&... args ) {
hoge( std::forward<Args>(args)... ); // パラメータパックの拡張は後述
}
template < typename... Types>
struct Hoge {
static constexpr unsigned long long size = sizeof...(Types);
};
int main(){
static_assert( Hoge<>::size == 0 , "" );
static_assert( Hoge<void,void,void>::size ==3 , "" );
}
#include <iostream>
template < typename T >
T g(T a){ return a*a; }
template < typename... Args >
auto print( Args const&... args ){
for(auto const& e : { args... })
std::cout << e << std::endl;
}
template < typename... Args >
void f(Args... args){
print( g(args)... ); // 全てのパラメータにgを適用してprintに渡す
}
int main(){
f(1,2,3,4);
// output:
// 1
// 4
// 9
// 16
}
std::tuple<Args...> tup;
template < typename...Types >
std::tuple<std::decay_t<Types>...> // 全ての型にstd::decay_tを適用
make_tuple(Types&&... args){
return std::tuple<std::decay_t<Types>...>{ std::forward<Types>(args)... };
}
template < class... Args >
void f(Args... args); // <- ここ、パラメータパックはargs
template < class... Args > // <- もちろんここ、パラメータパックはArgs
struct Hoge {};
template < template < class... > class T > // <- こういうやつです
struct Hoge {};
template < class... Args >
void print_all(Args&&... args){
// 実装
}
std::tuple<Args...> tup;
my_make_tuple(1, 2, 3.0);
template < typename ...Args >
void func( Args ...args ) {}
int main(){
func( 1, 3.0, 'a' ); // OK func<int,double,char>
func(); // OK func<>
}
int arr[] = { args... };
print(args)...
template < class... Bases >
class Derived : Bases...; // <- ここ
print(args)...
template < class... Bases >
class Derived : Bases... {
Derived(Bases... bases)
: Bases(bases)... {} // <- ここ
};
[](auto... args){
return [args...](auto ){
// ^~~~~~~ここ
};
};
template <typename... T>
struct A : T... {
using T::operator()...; // 受け取ったクラスのoperator()を全て使えるようにする
};
template < class... Args >
void print_all(Args&&... args){
// 実装
}
template < class... Pack >
void func(Pack... pack){
// ...
}
template < typename T >
void print(T const& a){
std::cout << a << std::endl;
}
( pack op ... ) //(1)
( ... op pack ) //(2)
( pack op ... op init ) //(3)
( init op ... op pack ) //(4)
namespace std {
template <class T, T... I>
struct integer_sequence {
using value_type = T;
static constexpr size_t size() noexcept { return sizeof...(I); }
};
}
namespace std {
template <size_t... I>
using index_sequence = integer_sequence<size_t, I...>;
}
print_all(1,2,3);
E_1 + (... + (E_{N-1} + E_N))
print(1), print(2), print(3)
// OK
template < typename Head, typename... Tail>
struct Hoge {};
// コンパイルエラー!パラメータパックは最後に置かなければならない
template < typename... Init, typename Last>
struct Fuga {};
Args... = int, int, double
(void(適用したい関数(args)),0)...
(((E_1 + E_2) + E_3 ) + ... ) + E_n
auto old_sum1(){
return 0;
}
template<typename T1, typename... T>
auto old_sum1(T1 s, T... ts){
return s + old_sum1(ts...);
}
template < typename T, typename... Tail >
constexpr T old_sum2( T&& head, Tail&&... tail ){
T result = head;
using swallow = std::initializer_list<int>;
(void)swallow{ (void(result += tail),0)... };
return result;
}
template < typename T, typename... Args2 >
static constexpr T eval( left_t<Indeces,T>... args1, Args2... args2)
// ↑ この部分。引数左側にTが展開されて残りを推論する
template<typename... T>
auto fold_sum_1(T... s){
return (... + s);
}
#include <map>
#include <utility>
#include <tuple>
struct Point3D {
Point3D(int a, int b,int c)
: x{a}, y{b}, z{c} {}
int x;
int y;
int z;
};
int main(){
std::pair<Point3D, int> p1{ std::piecewise_construct,
std::forward_as_tuple( 1,2,3 ),
std::forward_as_tuple( 1 )
};
}
template<typename... T>
auto fold_sum_2(T... s){
return (1 + ... + s);
}
#include <array>
#include <tuple>
#include <utility>
#include <iostream>
namespace cranberries {
namespace cranberries_magic {
template < typename Tuple, size_t ...I >
std::array<int,sizeof...(I)>
tuple_print_impl(Tuple&& t, std::index_sequence<I...>){
return {{ (void( std::cout << std::get<I>(t) << std::endl ), 0)... }};
}
} // ! namespace cranberries_magic
template < typename Tuple >
void tuple_print(Tuple&& t){
cranberries_magic::tuple_print_impl(std::forward<Tuple>(t),
std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>{});
}
} // ! namespace cranberries
int main(){
cranberries::tuple_print(std::make_tuple(1,2,3,4,5));
}
+ - * / % ^ & | = < > << >> += -= *= /= %= ^= &= |= <<= >>= == != <= >= && || , .* ->*
template < typename T >
T max( T const& a, T const& b ){
return a < b ? b : a;
}
template < typename Head, typename... Tail >
auto max( Head&& head, Tail&&... tail ){
return max( std::forward<Head>(head), max( std::forward<Tail>(tail...) ) );
}
#include <map>
#include <utility>
#include <tuple>
struct Point3D {
Point3D(int a, int b,int c)
: x{a}, y{b}, z{c} {}
int x;
int y;
int z;
};
int main(){
std::pair<Point3D, int> p1{ std::piecewise_construct,
std::forward_as_tuple( 1,2,3 ),
std::forward_as_tuple( 1 )
};
}
#include <initializer_list>
#include <algorithm>
namespace cranberries {
template < typename T >
T max( std::initializer_list<T> il ){
return *std::max_element(il.begin(), il.end());
}
} // ! namespace cranberries
int main(){
cranberries::max({1,2,3,4,5,6});
std::max({1,2,3,4,5,6});
// 可変長はないよ
//std::max(1,2,3,4,5,6);
}
template < class... Args >
void print_all(Args&&... args){
// 実装
}
template < typename T >
void print(T const& a){
std::cout << a << std::endl;
}
#include <tuple>
// OK
// 第1引数からパラメータパックTypesが推論される
template < typename ...Types, typename T >
void ok1(std::tuple<Types...>, T)
{}
// NG
// Argsの要素数をコンパイラが判断できない
// 引数の最後にパラメータパックを置かなければならない
template < typename ...Args, typename T >
void ng(Args..., T){}
// OK
// 引数の最後にパラメータパックが展開されているので可
template < typename ...Args, typename T >
void ok2(T, Args...){}
int main(){
// OK
// Args = [int,double,char], T = int
// tuple<int,double,char>からパラメータパックが推論される
ok1(std::make_tuple(1,1.0,'a'), 1);
// 推論できません
// ng1(1,2,3,4);
// OK
// T = int, Args = [int,int,int]
ok2(1,2,3,4);
}
std::tuple<Args...> 👉 std::tuple<int, int, double>
// NG
// 引数のどこまでがLeftでどこからRightなのかわからない
template < typename... Left, typename... Right >
void ng2(Left... left, Right... right){
f(left...);
g(right...);
}
print(args)...
print_all(1,2,3);
print(1), print(2), print(3)
(void(適用したい関数(args)),0)...
#include <iostream>
#include <initializer_list>
template < typename T >
void print(T const& a){
std::cout << a << std::endl;
}
template < typename... Args >
void print_all(Args... args){
using swallow = std::initializer_list<int>;
(void)swallow{ (void( print(args) ), 0)... };
}
int main(){
print_all(1,2,3,4,5,6);
}
#include <initializer_list>
#include <utility>
#include <type_traits>
namespace cranberries {
template < typename T, typename... Tail >
constexpr std::decay_t<T> max( T&& head, Tail&&... tail ){
std::decay_t<T> result = head;
using swallow = std::initializer_list<int>;
(void)swallow{ (void(result = result < tail ? tail : result),0)... };
return result;
}
} // ! namespace cranberries
int main(){
constexpr auto x = cranberries::max(1,2,3,4,5);
static_assert(x==5,"");
}
namespace std {
template <class T, T... I>
struct integer_sequence {
using value_type = T;
static constexpr size_t size() noexcept { return sizeof...(I); }
};
}
namespace std {
template <size_t... I>
using index_sequence = integer_sequence<size_t, I...>;
}
namespace std {
template <class T, T N>
using make_integer_sequence = integer_sequence<T, 0, 1, …, N - 1>;
}
namespace std {
template <size_t N>
using make_index_sequence = make_integer_sequence<size_t, N>;
}
namespace std {
template <class... T>
using index_sequence_for = make_index_sequence<sizeof...(T)>;
}
#include <array>
#include <tuple>
#include <utility>
#include <iostream>
namespace cranberries {
namespace cranberries_magic {
template < typename Tuple, size_t ...I >
std::array<int,sizeof...(I)>
tuple_print_impl(Tuple&& t, std::index_sequence<I...>){
return {{ (void( std::cout << std::get<I>(t) << std::endl ), 0)... }};
}
} // ! namespace cranberries_magic
template < typename Tuple >
void tuple_print(Tuple&& t){
cranberries_magic::tuple_print_impl(std::forward<Tuple>(t),
std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>{});
}
} // ! namespace cranberries
int main(){
cranberries::tuple_print(std::make_tuple(1,2,3,4,5));
}
#include <array>
#include <utility>
namespace cranberries {
namespace cranberries_magic {
template < size_t, class T = void >
struct left{
using type = T;
};
template < size_t N, class T >
using left_t = typename left<N,T>::type;
template < class >
struct sum_impl;
template < size_t... Indeces >
struct sum_impl<std::index_sequence<Indeces...>> {
template < typename T, typename... Args2 >
static constexpr T eval( left_t<Indeces,T>... args1, Args2... args2){
return sum_impl<std::make_index_sequence<sizeof...(args1)/2>>::template eval<T>(args1...)
+ sum_impl<std::make_index_sequence<sizeof...(args2)/2>>::template eval<T>(args2...);
}
};
template <>
struct sum_impl<std::index_sequence<>> {
template < typename T, typename... Args2 >
static constexpr T eval( T arg ){
return arg;
}
};
} // ! namespace cranberries_magic
template < typename Head, typename... Tail >
constexpr auto sum(Head head, Tail... tail){
return cranberries_magic::sum_impl<
std::make_index_sequence<(sizeof...(Tail)+1)/2>
>:: template eval<std::decay_t<Head>>(head, tail...);
}
} // ! namespace cranberries
int main(){
constexpr auto x = cranberries::sum(1,2,3,4,5,6,7,8,9,10,11); // 66
static_assert(x==66,"");
}
// テンプレートパラメータパックのパラメータすべてに
// const& を付加して
// パラメータパックArgsの全てのパラメータを、
// constな左辺値参照として受け取る
template < typename ...Args >
void f( const Args&... args ) {}
args... = 1, 2, 3.0
template < size_t, class T=void >
struct left{
using type = T;
};
template < typename T, typename... Args2 >
static constexpr T eval( left_t<Indeces,T>... args1, Args2... args2)
// ↑ この部分。引数左側にTが展開されて残りを推論する
template <>
struct sum_impl<std::index_sequence<>> {
template < typename T, typename... Args2 >
static constexpr T eval( T arg ){
return arg;
}
};
// NG
// 引数のどこまでがLeftでどこからRightなのかわからない
template < typename... Left, typename... Right >
void ng2(Left... left, Right... right){
f(left...);
g(right...);
}
#include <tuple>
#include <utility>
#include <iostream>
#include <initializer_list>
namespace cranberries {
namespace cranberries_magic {
template <class F, class Tuple, std::size_t... I>
constexpr decltype(auto) apply_impl( F&& f, Tuple&& t, std::index_sequence<I...> )
{
return f(std::get<I>(t)...);
}
} // ! namespace cranberries_magic
template <class F, class Tuple>
constexpr decltype(auto) apply(F&& f, Tuple&& t)
{
return cranberries_magic::apply_impl(std::forward<F>(f), std::forward<Tuple>(t),
std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>{});
}
} // ! namespace cranberries
template < typename T >
void print(T const& a){
std::cout << " " << a;
}
template < typename... Args >
void f(Args... args) {
std::cout << "called f with :";
using swallow = std::initializer_list<int>;
(void)swallow{ (void( print(args) ), 0)... };
std::cout << std::endl;
}
template < typename... Args >
void g(Args... args) {
std::cout << "called g with :";
using swallow = std::initializer_list<int>;
(void)swallow{ (void( print(args) ), 0)... };
std::cout << std::endl;
}
// OK
// 引数のどこまでがLeft,どこからRightなのかが
// コンパイラにもわかるように
// tupleに固めて区切る
template < typename... Left, typename... Right >
void ok( std::tuple<Left...> largs, std::tuple<Right...> rargs){
// tupleの要素を関数に展開するためにapply関数をつかう
// もっとちゃんとしたものがC++17に採択決定している
cranberries::apply([](auto... args){ f(args...); }, largs);
cranberries::apply([](auto... args){ g(args...); }, rargs);
}
int main(){
// forward_as_tupleで引数を完全転送して優勝!
ok( std::forward_as_tuple( 1,2,3 ),
std::forward_as_tuple( 4,5,6 ) );
}
#include <map>
#include <utility>
#include <tuple>
struct Point3D {
Point3D(int a, int b,int c)
: x{a}, y{b}, z{c} {}
int x;
int y;
int z;
};
int main(){
std::pair<Point3D, int> p1{ std::piecewise_construct,
std::forward_as_tuple( 1,2,3 ),
std::forward_as_tuple( 1 )
};
}
#include <map>
#include <utility>
#include <tuple>
class Point3D {
public:
Point3D(int a, int b,int c)
: x_{a}, y_{b}, z_{c} {}
int x() const { return x_; }
int y() const { return y_; }
int z() const { return z_; }
private:
int x_;
int y_;
int z_;
};
bool operator <(const Point3D& a, const Point3D& b){
return std::forward_as_tuple(a.x(), a.y(), a.z()) < std::forward_as_tuple(b.x(), b.y(), b.z());
}
int main(){
std::map<Point3D, int> hash{};
hash.emplace(std::piecewise_construct,
std::forward_as_tuple( 1,2,3 ),
std::forward_as_tuple( 1 )
);
}
reverse_apply(f, make_tuple(1,2,3,4));
#include <tuple>
#include <type_traits>
#include <utility>
#include <initializer_list>
#include <iostream>
namespace cranberries {
namespace cranberries_magic {
template < class F
, class Tuple
, size_t... Indices
, size_t Last = std::tuple_size_v<std::decay_t<Tuple>>-1 >
constexpr decltype(auto)
reverse_apply_impl(F&& f, Tuple&& t, std::index_sequence<Indices...>){
return f(std::get<Last-Indices>(t)...);
}
}
template < class F
, class Tuple >
constexpr decltype(auto)
reverse_apply(F&& f, Tuple&& t){
return cranberries_magic::reverse_apply_impl(std::forward<F>(f)
, std::forward<Tuple>(t)
, std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>{});
}
}
int main(){
auto println = [](auto&& head, auto&&... tail) -> void {
std::cout << head;
(void)std::initializer_list<int>{
(void(std::cout << " " << tail), 0)...
};
std::cout << std::endl;
};
std::apply(println, std::make_tuple(1,2,3,"4"));
cranberries::reverse_apply(println, std::make_tuple(1,2,3,"4"));
}
template <typename ...Args>
void f( Args&&... args ) {
hoge( std::forward<Args>(args)... ); // パラメータパックの拡張は後述
}
{ args... } 👉 { 1, 2, 3.0 }
template < typename ...Args >
void func( Args ...args ) {}
int main(){
func( 1, 3.0, 'a' ); // OK func<int,double,char>
func(); // OK func<>
}
// OK
template < typename Head, typename... Tail>
struct Hoge {};
// コンパイルエラー!パラメータパックは最後に置かなければならない
template < typename... Init, typename Last>
struct Fuga {};
#include <tuple>
// OK
// 第1引数からパラメータパックTypesが推論される
template < typename ...Types, typename T >
void ok1(std::tuple<Types...>, T)
{}
// NG
// Argsの要素数をコンパイラが判断できない
// 引数の最後にパラメータパックを置かなければならない
template < typename ...Args, typename T >
void ng(Args..., T){}
// OK
// 引数の最後にパラメータパックが展開されているので可
template < typename ...Args, typename T >
void ok2(T, Args...){}
int main(){
// OK
// Args = [int,double,char], T = int
// tuple<int,double,char>からパラメータパックが推論される
ok1(std::make_tuple(1,1.0,'a'), 1);
// 推論できません
// ng1(1,2,3,4);
// OK
// T = int, Args = [int,int,int]
ok2(1,2,3,4);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment