引数の型だけ違う(関数オブジェクト|関数)の処理をtemplateで切り分ける

の続編。関数オブジェクトだけじゃなくて、関数でもOKにしてみた。ラムダ式でも動いた。関数ポインタが混ざってくると混乱する。

#include<iostream>
#include<vector>
#include<type_traits>

//適当なfunction traits
template<typename T_> struct function_traits{};
template<typename C_, typename R_, typename Arg_>
struct function_traits<R_(C_::*)(Arg_) const>{ typedef Arg_ argument; };
template<typename R_, typename Arg_>
struct function_traits<R_(Arg_)>{ typedef Arg_ argument; };

//Tag(Vector or Double)
struct tagV{};
struct tagD{};
//Tag(Class(Functor) or Function)
struct tagC{};
struct tagF{};

template<typename T_>
struct call_traits
{
	typedef tagC object_type;
	typedef typename std::conditional<std::is_same<typename function_traits<decltype(&T_::operator())>::argument, double>::value, tagD, tagV>::type argument_type;
};
template<class R_, class Arg_>
struct call_traits<R_(Arg_)>
{
	typedef tagF object_type;
	typedef typename std::conditional<std::is_same<Arg_, double>::value, tagD, tagV>::type argument_type;
};
//タグに応じた実際の処理
template<typename T_>
void call_detail(const T_ & t, std::vector<double> x, tagV){ t(x); }
template<typename T_>
void call_detail(const T_ & t, std::vector<double> x, tagD){ t(x[0]); }

template<class T_>
void call(const T_ & t, std::vector<double> x)
{
	call_detail(t, x, call_traits<T_>::argument_type());
}
//引数の型が違うクラス&関数
void a(double){ std::cout << "I'm function A" << std::endl; }
void b(double){ std::cout << "I'm function B" << std::endl; }
struct A{ void operator()(double x) const{ std::cout << "I'm class A:" << x << std::endl; } };
struct B{ void operator()(std::vector<double> x)const{ std::cout << "I'm class B:" << x[0] << ", " << x[1] << ", " << x[2] << std::endl; } };
//メイン
int main()
{
	std::vector<double> xxx = { 1, 2, 3 };
	call(A(), xxx);
	call(B(), xxx);
	call(a, xxx);
	call(b, xxx);
	call([](double x){std::cout << "I'm lambda." << std::endl; }, xxx);
	return 0;
}

実行結果

I'm class A:1
I'm class B:1, 2, 3
I'm function A
I'm function B
I'm lambda.