#include <iostream>
int func3(int n1, int n2, int n3) {
std::cout << n1 << '+' << n2 << '+' << n3 << '=' << std::flush;
return n1+n2+n3;
}
int func2(int n1, int n2) {
std::cout << n1 << '+' << n2 << '=' << std::flush;
return n1+n2;
}
int func1(int n1) {
std::cout << n1 << '=' << std::flush;
return n1;
}
int func0(void) {
return 0;
}
int main(void){
std::cout << func3(1,2,3) << std::endl;
std::cout << func2(1,2) << std::endl;
std::cout << func1(1) << std::endl;
std::cout << func0() << std::endl;
return 0;
}
実行すると、
1+2+3=6
1+2=3
1=1
0
こんな感じ。
このmain()
関数の部分が、書くのが面倒くさいので、
int main(void){
std::cout << stepargs(func3) << std::endl;
std::cout << stepargs(func2) << std::endl;
std::cout << stepargs(func1) << std::endl;
std::cout << stepargs(func0) << std::endl;
return 0;
}
とだけ書いたら、自動的に連番整数を引数として関数を実行してほしい、というのがお題。
#include <type_traits>
template<int N, typename Func, typename ...Args>
auto add_steparg(typename std::enable_if<!N, Func>::type func, Args... args) {
return func(args...);
}
template<int N, typename Func, typename ...Args>
auto add_steparg(typename std::enable_if<N, Func>::type func, Args... args) {
return add_steparg<N-1, Func>(func, N, args...);
}
template<typename Ret, typename ...Args>
Ret stepargs(Ret (*func)(Args...)) {
return add_steparg<sizeof...(Args), decltype(func)>(func);
}
これをmain()
関数の前に書いてやればOK。
ここでは関数呼び出しのために、再帰でテンプレート引数を伸ばしていっている。
伸ばす長さの目印として、template<int N>
にsizeof...(Args)
を最初に渡しておいて、再帰の度に減らして、ゼロになったら目的の関数を呼び出す。
しかし可変長テンプレート+可変長引数とstd::enable_if
との相性悪い・・・。
テンプレート引数や、関数引数の最後に、std::enable_if
を使ったデフォルト値付きのダミーの引数を付けられないので、無理矢理どこかに押し込まないといけない。
#include <type_traits>
template<int N, typename Func, typename ...Args>
auto add_steparg(typename std::enable_if<!N>::type*, Func func, Args... args) {
return func(args...);
}
template<int N, typename Func, typename ...Args>
auto add_steparg(typename std::enable_if<N>::type*, Func func, Args... args) {
return add_steparg<N-1>(0, func, N, args...);
}
template<typename Ret, typename ...Args>
Ret stepargs(Ret (*func)(Args...)) {
return add_steparg<sizeof...(Args)>(0, func);
}
関数引数の最初にデフォルト値なしのダミー引数を付けた方がシンプルかな。
当然呼び出し元にもダミーの引数を付けないといけないけれど。
その代わりに、テンプレート引数にFunc
を付けなくても推論してくれるようになった。
template<int N> struct add_steparg{
template<typename Func, typename ...Args>
static auto x(Func func, Args... args) {
return add_steparg<N-1>::x(func, N, args...);
}
};
template<> struct add_steparg<0>{
template<typename Func, typename ...Args>
static auto x(Func func, Args... args) {
return func(args...);
}
};
template<typename Ret, typename ...Args>
Ret stepargs(Ret (*func)(Args...)) {
return add_steparg<sizeof...(Args)>::x(func);
}
std::enable_if
を使わずに、テンプレート特殊化でやった場合。struct
がちょっと鬱陶しい。
ちなみに最適化MAXでコンパイルすると、単なる定数引数での関数呼び出しと同じになりました。なので実行時のオーバヘッドはゼロ。GCCすごい。
追記
if constexpr
が使えるなら、下記でも大丈夫の模様。
template<int N, typename Ret, typename Func, typename ...Args>
Ret add_steparg(Func func, Args... args) {
if constexpr(N) {
return add_steparg<N-1, Ret>(func, N, args...);
} else {
return func(args...);
}
}
template<typename Ret, typename ...Args>
Ret stepargs(Ret (*func)(Args...)) {
return add_steparg<sizeof...(Args), Ret>(func);
}