#可変長引数テンプレートとは
c++で使えるテンプレートの一つで、
template<typename... Args>
void func(Args... arg);
みたいなやつ。
このArgsとargはパラメータパックとよばれ、特定の場所で展開できる。(c++17からは畳み込み式によってほぼどこでも展開できる)
基本としてはタプルで展開する。
std::tuple<Args...> argument = {arg...};
#やりたかったこと
記事のタイトル通り、可変長引数テンプレートで特定の型のみを受け取りたかった。
調べてみたところinitializer_list使う方法があった。(http://secret-garden.hatenablog.com/entry/2016/06/10/230548)
//intのみで受け取る関数
template<typename... Args>
auto func(Args... arg) -> decltype(std::initializer_list<int>{arg...}, std::declval<void>());
まぁ、なんとか解決した...
しかし何か腑に落ちない。
これでコンパイルエラーを出すと [一致するオーバーロードされた関数が見つかりませんでした。]と言われる。
まあ目的の引数と違うのだから当然だ。
しかしできれば目的の引数が何なのかエラー文で教えて欲しい。
「じゃあ引数をinitializer_listにして明示的にすればいいのでは?」と思うかもしれないが、私は引数に**{}**をどうしてもつけたくなかった。(我儘)
とりあえずstatic_assertでいい感じにコンパイルから弾こうと思ったが目的のメタ関数が見つからなかった。
#結果
なんか気持ち悪いのができた。
#include <type_traits>
#include <tuple>
#include <utility>
template<size_t Index, typename... Args>
class checkSameType {
public:
static constexpr bool check(){
if (!std::is_same<decltype(std::get<Index>((std::declval<std::tuple<Args...>>()))), decltype(std::get<Index - 1>((std::declval<std::tuple<Args...>>())))>::value)
return false;
return checkSameType<Index - 1, Args...>::check();
}
};
template<typename... Args>
class checkSameType <0, Args...> {
public:
static constexpr bool check() {
return true;
}
};
template<typename... Args>
constexpr bool check_same_type() {
return checkSameType<(sizeof...(Args)) - 1, Args...>::check();
}
内容としてはis_sameでパラメータパックの引数の型をかたっぱしから再帰によって比較して、違うのが混じっていたらfalseを返す定数式になっている。
これで目的のstatic_assertでコンパイルエラーが出せる!
static_assert(check_same_type<int&&, Args...>(), "Argument must be 'int'.");
タプルのせいで右辺値参照と比較しないといけないのが難点。
というかもっと頭のいい方法ないですかね?
#2019/07/18追記
コメントで教えた貰ったサイトにもっと簡潔な記述法がありました。
static_assert(std::conjunction<std::is_same<int, Args>...>::value, "Argument must be 'int'.");
**is_same < int, Args >...**でパラメータパックの展開ができるとは目から鱗でした。