เป็นไปได้หรือไม่ที่จะหาประเภทพารามิเตอร์และประเภทผลตอบแทนของแลมด้า


136

เมื่อพิจารณาแลมบ์ดาเป็นไปได้ไหมที่จะหาประเภทพารามิเตอร์และประเภทการส่งคืน ถ้าใช่อย่างไร

โดยพื้นฐานแล้วฉันต้องการ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

คำตอบ:


163

ตลกดีฉันเพิ่งเขียนการfunction_traitsใช้งานตามความเชี่ยวชาญเทมเพลตบนแลมบ์ดาใน C ++ 0xซึ่งสามารถให้ประเภทพารามิเตอร์ เคล็ดลับตามที่อธิบายไว้ในคำตอบในคำถามนั้นคือการใช้ของแลมบ์ดา 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
ลักษณะที่สมบูรณ์จะใช้ความเชี่ยวชาญสำหรับ non- constสำหรับ lambda ที่ประกาศไว้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/…สำหรับวิธีแก้ปัญหาลิงค์เสีย: ลองค้นหาจากรูทลุค
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 จะตอบสนองวัตถุประสงค์ เนื่องจาก ideone ดูเหมือนจะไม่ให้การเพิ่มในโหมด C ++ 11 ฉันไม่สามารถโพสต์รหัสจริงได้ขออภัย


1
ฉันคิดว่ามันเป็นการเริ่มต้นที่ดี +1 สำหรับสิ่งนั้น ตอนนี้เราต้องทำงานกับประเภทฟังก์ชันเพื่อดึงข้อมูลที่ต้องการ (ตอนนี้ฉันไม่ต้องการใช้ Boost เพราะฉันต้องการเรียนรู้เนื้อหาต่างๆ)
Nawaz

6

วิธีการเฉพาะทางที่แสดงในคำตอบของ @Kenny ™สามารถขยายได้เพื่อให้ครอบคลุมทุกกรณีรวมถึง lambdas ที่แปรผันและเปลี่ยนแปลงได้:

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 ไม่ได้ถูกปรับสำหรับตัวแปรoperator()s is_variadicแต่หนึ่งยังสามารถพิจารณา


1

คำตอบที่ให้โดย @KennyTMs ใช้งานได้ดีอย่างไรก็ตามหากแลมบ์ดาไม่มีพารามิเตอร์การใช้ดัชนี arg <0> จะไม่รวบรวม หากใครกำลังมีปัญหานี้ฉันมีวิธีแก้ปัญหาง่ายๆ (ง่ายกว่าการใช้โซลูชันที่เกี่ยวข้องกับ SFINAE นั่นคือ)

เพียงเพิ่มโมฆะต่อท้ายทูเปิลในโครงสร้างอาร์กิวเมนต์หลังประเภทอาร์กิวเมนต์ตัวแปร กล่าวคือ

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.