できないこともたくさんある
実行イメージ
auto f=[](int){};
using type = get_arg_type_t<decltype(f), // 推測される関数
int,char,A>; //推測できる型
実装原理
自由な型へ変換可能なクラスを作り、それを利用して関数を呼び出す
その際副作用を利用してどのような型に変換されるかを値として保存
それを型に戻す
実装
(説明の都合上、今回推定できる型はAとBのみとする)
struct A{};
struct B{};
型をidに変換する方法、idから型に変換する方法を何らかの方法で実装する
template<class T>struct wrap{
using type=T;
};
template<int>struct Int{};
constexpr int toID(wrap<A>){return 1;}
constexpr int toID(wrap<B>){return 2;}
constexpr wrap<A> toType(Int<1>){return {};};
constexpr wrap<B> toType(Int<2>){return {};};
こんな型を用意する
型の変換時にidに変換先のidが保存される
struct ubiq{
int id=0;
template<class T>
constexpr operator T(){
id = toID(wrap<T>{});
return {};
}
};
関数に適用しidを取り出す
template<class F>
constexpr int get_arg_type_id(F f){
ubiq a;
f(a);
return a.id;
}
idから型に戻す
template<class F>
constexpr auto get_arg_type(F f)
{
constexpr int id = get_arg_type_id(f);
return toType(Int<id>{});
}
完成
int main()
{
auto a=get_arg_type([](A){});
static_assert(std::is_same<decltype(a)::type,A>{},"");
auto b=get_arg_type([](B){});
static_assert(std::is_same<decltype(b)::type,B>{},"");
}
推定の制限
- 推定できる型は明示的に指定する必要があります
- この関数にとって未知の型(上の例だとAB以外の型)は推定はできません
- 「型をidに変換する」機能をあらかじめ実装しておく必要があるため
- 何らかの方法(ハックとか魔法とか)でコンパイル時にすべての型に対しidを振れ、かつそのidから型に戻せるなら可能です
- constexprな(デフォルト)コンストラクタを持った型しか推測できません
- ubiqで変換先の型を返す必要があるため
- 変換先の型を生成する手段があるならばデフォルトコンストラクタを使う必要はありません
- 例えば
make_foo<T>()
やT{0}
で推定できる型が生成できるのならばそれでも良い
- 例えば
参考
magic_getの実装
https://github.com/apolukhin/magic_get