Lambda'nın parametre tipini ve dönüş tipini bulmak mümkün müdür?


135

Bir lambda verildiğinde, parametre türünü ve dönüş türünü bulmak mümkün müdür? Evetse, nasıl?

Temel olarak, lambda_traitsaşağıdaki şekillerde kullanılabilmesini istiyorum :

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

Arkasındaki motivasyon lambda_traits, bir lambda'yı bağımsız değişken olarak kabul eden bir işlev şablonunda kullanmak istiyorum ve bunun işlev içinde parametre türü ve dönüş türü olduğunu bilmem gerekiyor:

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!
   //...
}

Şimdilik, lambda'nın tam olarak bir argüman aldığını varsayabiliriz.

Başlangıçta şu şekilde çalıştım std::function:

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

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

Ama belli ki hata verirdi. Bu yüzden onu TLambdaişlev şablonunun sürümüne değiştirdim ve işlevin std::functioniçindeki nesneyi oluşturmak istiyorum (yukarıda gösterildiği gibi).


Parametre türünü biliyorsanız, bu dönüş türünü bulmak için kullanılabilir. Parametre türünü nasıl anlayacağımı bilmiyorum.
Mankarse

Fonksiyonun tek bir argüman aldığı var mı?
iammilind

1
"parametre türü" Ancak rastgele bir lambda işlevinin parametre türü yoktur. Herhangi bir sayıda parametre alabilir. Bu nedenle, herhangi bir nitelik sınıfının, parametreleri konum indekslerine göre sorgulayacak şekilde tasarlanması gerekir.
Nicol Bolas

@iammilind: Evet. şimdilik bunu kabul edebiliriz.
Nawaz

@NicolBolas: Şimdilik lambda'nın tam olarak bir argüman aldığını varsayabiliriz.
Nawaz

Yanıtlar:


160

Komik, sadece parametre türlerini verebilecek C ++ 0x lambda üzerinde bir şablon Uzmanlaşma dayalı bir function_traitsuygulama yazdım . Bu sorunun cevabında tarif edilen hile, lambda'nın kullanılmasıdır . 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;
}

Bu çözümü Not gelmez gibi genel lambda için çalışmak [](auto x) {}.


Heh, sadece yazıyordum. tuple_elementYine de düşünmedim , teşekkürler.
GManNickG

@ GMan: Yaklaşımınız tam olarak aynı değilse, lütfen o zaman gönderin. Bu çözümü test edeceğim.
Nawaz

3
Tam bir özellik const, belirtilen lambda mutable( []() mutable -> T { ... }) için olmayan bir uzmanlık da kullanır .
Luc Danton

1
@Andry, operator()bu uygulamada olmayan (potansiyel olarak) birden fazla aşırı yüklemeye sahip fonksiyon nesneleri ile temel bir sorundur . autobir tür değil, bu yüzden cevap olamaztraits::template arg<0>::type
Caleth

1
@helmesjo sf.net/p/tacklelib/tacklelib/HEAD/tree/trunk/include/tacklelib/… Kırık bağlantılar için bir çözüm olarak: kökünden arama yapmayı deneyin, Luke.
Andry

11

Bunun kesinlikle standart uygun olduğundan emin olmasam da , ideone aşağıdaki kodu derledi:

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, "" );
}

Ancak, bu yalnızca işlev türünü sağlar, bu nedenle sonuç ve parametre türlerinin bundan çıkarılması gerekir. Eğer kullanabiliyorsa boost::function_traits, result_typeve arg1_type amacını bir araya gelecek. İdeone C ++ 11 modunda destek sağlamadığından, gerçek kodu gönderemedim, üzgünüm.


1
Bence bu iyi bir başlangıç. Bunun için +1. Şimdi gerekli bilgileri çıkarmak için işlev türü üzerinde çalışmamız gerekiyor. (Şu andan itibaren Boost'u kullanmak istemiyorum, çünkü maddeleri öğrenmek istiyorum ).
Nawaz

6

@KennyTM'nin cevabında gösterilen uzmanlaşma yöntemi, varyasyon ve değişken lambdalar dahil tüm vakaları kapsayacak şekilde genişletilebilir:

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 .

Düzlüğün varyasyonlar için ayarlanmadığını unutmayın operator(). Bunun yerine kişi de düşünebilir is_variadic.


1

@KennyTMs tarafından verilen cevap harika çalışıyor, ancak lambda'nın parametresi yoksa, <arg dizinini kullanarak derleme yapmaz. Başka biri bu sorunu yaşıyor olsaydı, basit bir çözüm var (SFINAE ile ilgili çözümleri kullanmak daha basit, yani).

Değişken argüman türlerinden sonra arg yapısındaki grubun sonuna void eklemeniz yeterlidir. yani

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

arity, şablon parametrelerinin gerçek sayısına bağlı olmadığından, gerçek yanlış olmaz ve eğer 0 ise, en azından arg <0> hala var olacaktır ve bununla ne yapacağınızı yapabilirsiniz. Zaten dizini aşmamayı planlıyorsanızarg<arity-1> , mevcut uygulamanıza müdahale etmemelidir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.