あごみつ さんが書きました:
引数として渡されたラムダ式の戻り値と引数の型を調べることはできますか?
結論から述べると、ラムダ式(関数オブジェクト)から直接 戻り値と仮引数の型を調べることはできません。
回りくどく、擬似的に調べることはできますが、その場合も関数ポインタの型を明示しないとできません。(これはSTLのfunctionと同様です)
コード:
#include <iostream>
#include <string>
#include <vector>
#include <tuple>
#include <memory>
#include <type_traits>
#include <functional>
using namespace std;
template<typename... Args>
class Type
{
public:
static inline auto get() -> std::vector<const std::type_info*>
{
std::vector<const std::type_info*> type_list;
type_list.reserve(std::tuple_size<Tuple_Type>::value);
get<std::tuple_size<Tuple_Type>::value - 1>(type_list);
return move(type_list);
}
private:
template<std::size_t index>
static inline void get(std::vector<const std::type_info*>& type_list)
{
using Element_Type = typename std::tuple_element<index, Tuple_Type>::type;
type_list.emplace_back(&typeid(Element_Type));
get<index - 1>(type_list);
}
template<>
static inline void get<0>(std::vector<const std::type_info*>& type_list)
{
using Element_Type = typename std::tuple_element<0, Tuple_Type>::type;
type_list.emplace_back(&typeid(Element_Type));
}
template<>
static inline void get<-1>(std::vector<const std::type_info*>& type_list)
{
type_list.emplace_back(typeid(void).name());
}
public:
using Tuple_Type = std::tuple<Args...>;
};
template<typename R, typename ...Args>
auto func(R(*)(Args...)) -> R
{
cout << "戻値の型:" << typeid(R).name() << "\n"
<< "引数の型:";
std::vector<const std::type_info*> v = Type<Args...>::get();
for (auto type : Type<Args...>::get())
cout << type->name() << " "; cout << "\n" << endl;
return 0;
}
template<typename Function_Type>
auto func(Function_Type f) -> decltype(func(f))
{
return func(f);
}
int main()
{
func<int(int, int)>([](int x, int y) { return x + y; });
func<double(float, float, float)>([](float x, float y, float z) -> double { return x / y / z; });
return 0;
}
また、ラムダ式から関数ポインタへの暗黙の型変換は、templateの展開時に 暗黙に型を置き換えてくれる訳ではありません。
(暗黙に型変換できるかどうかを)コンパイラが判断するのは、 templateの展開後となります。
そのため、templateへラムダ式を渡すコードを記述しても関数ポインタ型として展開されることはありません。
コード:
#include <iostream>
using namespace std;
template<typename R, typename ...Args>
auto func(R(*)(Args...)) -> R
{
cout << typeid(R).name() << endl;
return 0;
}
using Function_Type = int(int, int);
auto hoge(Function_Type f) -> decltype(func(f))
{
return func(f);
}
int main()
{
//func([](int x, int y) { return x + y; }); //template側の仮引数の型が明示されていないため、型の不一致により暗黙の型変換どころかtemplateがインスタンス化できない。
hoge([](int x, int y) { return x + y; }); //仮引数の型が明示されているため、暗黙の型変換ができる。
return 0;
}
あと、もし他の言語と同じようなことをしようとされていましたら
ジェネリックプログラミングについて、他言語との仕様の違いについて調べてみてください。
それと、目的に寄りますが
ラムダ式指定時に実引数も同時に渡すのであれば、以下のような方法で動作させることはできます。
コード:
#include <iostream>
#include <functional>
#include <thread>
#include <future>
using namespace std;
template<typename Func, typename ...Args>
auto worker(Func&& func, Args&&... args) -> packaged_task<typename result_of<Func(Args...)>::type()>
{
return packaged_task<typename result_of<Func(Args...)>::type()>(bind(forward<Func>(func), forward<Args>(args)...));
}
int func(int a, int b, int c)
{
return a * b * c;
}
int main()
{
auto task1 = worker(func, 1, 2, 3);
auto f1 = task1.get_future();
thread(move(task1)).detach();
cout << f1.get() << endl;
auto task2 = worker([](int a, int b, int c){ return a * b * c; }, 4, 5, 6);
auto f2 = task2.get_future();
thread(move(task2)).detach();
cout << f2.get() << endl;
cin.ignore();
}