람다의 매개 변수 유형과 반환 유형을 알아낼 수 있습니까?


135

람다가 주어지면 매개 변수 유형과 반환 유형을 알아낼 수 있습니까? 그렇다면 어떻게?

기본적으로 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!
   //...
}

당분간 우리는 람다는 정확히 하나의 주장을 취한다고 가정 할 수 있습니다.

처음에는 std::function다음 과 같이 작업하려고 했습니다.

template<typename T>
A<T> f(std::function<bool(T)> fun)
{
   return A<T>(fun);
}

f([](int){return true;}); //error

그러나 분명히 오류가 발생합니다. 그래서 TLambda함수 템플릿의 버전으로 변경 하고 함수 std::function안에 객체 를 구성하려고 합니다 (위 그림 참조).


당신은 다음 매개 변수 유형을 알고 있다면 반환 유형을 파악하는 데 사용할 수 있습니다. 그래도 매개 변수 유형을 파악하는 방법을 모르겠습니다.
Mankarse

함수가 단일 인수를 취하는 것으로 가정합니까?
iammilind

1
"매개 변수 유형"그러나 임의의 람다 함수에는 매개 변수 유형이 없습니다. 여러 개의 매개 변수를 사용할 수 있습니다. 따라서 모든 특성 클래스는 위치 인덱스별로 매개 변수를 조회하도록 설계되어야합니다.
Nicol Bolas

@iammilind : 그렇습니다. 당분간, 우리는 그것을 가정 할 수 있습니다.
Nawaz

@NicolBolas : 당분간 람다는 정확히 하나의 인수를 취한다고 가정 할 수 있습니다.
Nawaz

답변:


160

재미있게, 방금 매개 변수 유형을 제공 할 수 있는 C ++ 0x의 람다에서 템플릿을 전문화하는 것을 기반으로 function_traits구현을 작성했습니다 . 그 질문에 대한 답변에서 설명한 것처럼 트릭 은 람다 를 사용하는 것 입니다 . decltypeoperator()

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) {} .


허, 나는 단지 이것을 쓰고 있었다. tuple_element그래도 생각하지 못했습니다 . 감사합니다.
GManNickG

@ GMan : 귀하의 접근 방식이 이와 정확히 일치하지 않으면 게시하십시오. 이 솔루션을 테스트하겠습니다.
Nawaz

3
전체 특성도 비에 대한 전문성을 사용 const하는 람다 선언에 대해, mutable( []() mutable -> T { ... }).
Luc Danton

1
@Andry operator()이것은이 구현 에 포함 되지 않은 (잠재적으로) 여러 개의 과부하가있는 함수 객체의 근본적인 문제입니다 . auto유형이 아니므로 답이 될 수 없습니다traits::template arg<0>::type
Caleth

1
@helmesjo sf.net/p/tacklelib/tacklelib/HEAD/tree/trunk/include/tacklelib/… 끊어진 링크에 대한 해결책으로 루트에서 검색해보십시오. Luke.
Andry

11

이것이 엄격한 표준 준수인지 확실하지 않지만 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 목적을 충족합니다. C ++ 11 모드에서 ideone이 부스트를 제공하지 않는 것 같으므로 실제 코드를 게시 할 수 없습니다. 죄송합니다.


1
좋은 출발이라고 생각합니다. +1입니다. 이제 필요한 정보를 추출하기 위해 함수 유형에 대해 작업해야합니다. ( 물건 을 배우고 싶을 때 현재 Boost를 사용하고 싶지 않습니다 ).
Nawaz

6

@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)

데모 .

arity는 variadic에 맞게 조정되지 않습니다 operator(). 대신 고려할 수도 있습니다 is_variadic.


1

@KennyTMs가 제공하는 답변은 훌륭하지만 람다에 매개 변수가 없으면 인덱스 arg <0>을 사용하여 컴파일되지 않습니다. 다른 사람 이이 문제를 겪고 있다면 간단한 솔루션을 가지고 있습니다 (SFINAE 관련 솔루션을 사용하는 것보다 간단합니다).

variadic 인수 유형 뒤에 arg 구조체의 튜플 끝에 void를 추가하십시오. 즉

template <size_t i>
    struct arg
    {
        typedef typename std::tuple_element<i, std::tuple<Args...,void>>::type type;
    };

arity는 실제 템플릿 매개 변수 수에 의존하지 않기 때문에 실제 값은 정확하지 않으며 0이면 최소한 arg <0>이 여전히 존재하며 원하는대로 수행 할 수 있습니다. 이미 색인 arg<arity-1>을 초과하지 않으려는 경우 현재 구현을 방해 하지 않아야합니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.