ラムダが与えられた場合、パラメータの型と戻り値の型を把握することは可能ですか?はいの場合、どのように?
基本的に、lambda_traits
これは、次の方法で使用できます。
auto lambda = [](int i) { return long(i*10); };
lambda_traits<decltype(lambda)>::param_type i; //i should be int
lambda_traits<decltype(lambda)>::return_type l; //l should be long
背後にある動機は、lambda_traits
は、引数としてラムダを受け取る関数テンプレートで、関数内のパラメーターの型と戻り値の型を知る必要があります。
template<typename TLambda>
void f(TLambda lambda)
{
typedef typename lambda_traits<TLambda>::param_type P;
typedef typename lambda_traits<TLambda>::return_type R;
std::function<R(P)> fun = lambda; //I want to do this!
//...
}
とりあえず、ラムダが引数を1つだけとると仮定できます。
最初は、std::function
as:
template<typename T>
A<T> f(std::function<bool(T)> fun)
{
return A<T>(fun);
}
f([](int){return true;}); //error
しかし、明らかにエラーが発生します。そこで、それを関数テンプレートのTLambda
バージョンに変更し、std::function
関数内のオブジェクト(上記を参照)。
おかしい、私は _function_traits
_実装 に基づいて書いたところです C++ 0xのラムダでのテンプレートの特化 パラメータタイプを指定できます。その質問の答えで説明されているトリックは、ラムダのoperator()
のdecltype
を使用することです。
_template <typename T>
struct function_traits
: public function_traits<decltype(&T::operator())>
{};
// For generic types, directly use the result of the signature of its 'operator()'
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const>
// we specialize for pointers to member function
{
enum { arity = sizeof...(Args) };
// arity is the number of arguments.
typedef ReturnType result_type;
template <size_t i>
struct arg
{
typedef typename std::Tuple_element<i, std::Tuple<Args...>>::type type;
// the i-th argument is equivalent to the i-th Tuple element of a Tuple
// composed of those arguments.
};
};
// test code below:
int main()
{
auto lambda = [](int i) { return long(i*10); };
typedef function_traits<decltype(lambda)> traits;
static_assert(std::is_same<long, traits::result_type>::value, "err");
static_assert(std::is_same<int, traits::arg<0>::type>::value, "err");
return 0;
}
_
このソリューションは、[](auto x) {}
のような汎用ラムダでは機能しないことに注意してください。
これが厳密に標準に準拠しているかどうかはわかりませんが、 ideone は次のコードをコンパイルしました。
template< class > struct mem_type;
template< class C, class T > struct mem_type< T C::* > {
typedef T type;
};
template< class T > struct lambda_func_type {
typedef typename mem_type< decltype( &T::operator() ) >::type type;
};
int main() {
auto l = [](int i) { return long(i); };
typedef lambda_func_type< decltype(l) >::type T;
static_assert( std::is_same< T, long( int )const >::value, "" );
}
ただし、これは関数タイプのみを提供するため、結果およびパラメータータイプをそこから抽出する必要があります。 boost::function_traits
を使用できる場合、result_type
およびarg1_type
が目的を満たします。 ideoneはC++ 11モードでブーストを提供しないように見えるため、実際のコードを投稿できませんでした、申し訳ありません。
@KennyTMsの回答に示されている特殊化メソッドは、可変および可変ラムダを含むすべてのケースをカバーするように拡張できます。
_template <typename T>
struct closure_traits : closure_traits<decltype(&T::operator())> {};
#define REM_CTOR(...) __VA_ARGS__
#define SPEC(cv, var, is_var) \
template <typename C, typename R, typename... Args> \
struct closure_traits<R (C::*) (Args... REM_CTOR var) cv> \
{ \
using arity = std::integral_constant<std::size_t, sizeof...(Args) >; \
using is_variadic = std::integral_constant<bool, is_var>; \
using is_const = std::is_const<int cv>; \
\
using result_type = R; \
\
template <std::size_t i> \
using arg = typename std::Tuple_element<i, std::Tuple<Args...>>::type; \
};
SPEC(const, (,...), 1)
SPEC(const, (), 0)
SPEC(, (,...), 1)
SPEC(, (), 0)
_
Demo .
可変個のoperator()
sのアリティは調整されないことに注意してください。代わりに、_is_variadic
_も検討できます。
@KennyTMsが提供する答えはうまく機能しますが、ラムダにパラメーターがない場合、インデックスarg <0>を使用してもコンパイルされません。他の誰かがこの問題を抱えていた場合、簡単な解決策があります(つまり、SFINAE関連のソリューションを使用するよりも簡単です)。
可変引数のタイプの後に、arg構造体のTupleの最後にvoidを追加するだけです。つまり.
template <size_t i>
struct arg
{
typedef typename std::Tuple_element<i, std::Tuple<Args...,void>>::type type;
};
アリティはテンプレートパラメータの実際の数に依存しないため、実際の値は不正確ではありません。0の場合、少なくともarg <0>が存在し、それを使用して何でもできます。インデックスを超えないように既に計画している場合arg<arity-1>
その後、現在の実装に干渉してはなりません。