2015-02-26 14 views
121

के रूप में लैम्ब्डा पास करना क्या एक लम्बा फ़ंक्शन को फ़ंक्शन पॉइंटर के रूप में पास करना संभव है? यदि ऐसा है, तो मुझे कुछ गलत तरीके से करना होगा क्योंकि मुझे संकलन त्रुटि मिल रही है।फ़ंक्शन पॉइंटर

In function 'int main()': 
17:31: error: the value of 'x' is not usable in a constant expression 
16:9: note: 'int x' is not const 
17:53: error: no matching function for call to 'Decide::Decide(<brace-enclosed initializer list>)' 
17:53: note: candidates are: 
9:5: note: Decide::Decide(DecisionFn) 
9:5: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'DecisionFn {aka bool (*)()}' 
6:7: note: constexpr Decide::Decide(const Decide&) 
6:7: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'const Decide&' 
6:7: note: constexpr Decide::Decide(Decide&&) 
6:7: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'Decide&&' 

को पचाने के लिए एक त्रुटि संदेश से एक है बिल्ली यही कारण है, लेकिन मुझे लगता है कि मैं क्या कर रहा हूँ:

निम्न उदाहरण

using DecisionFn = bool(*)(); 

class Decide 
{ 
public: 
    Decide(DecisionFn dec) : _dec{dec} {} 
private: 
    DecisionFn _dec; 
}; 

int main() 
{ 
    int x = 5; 
    Decide greaterThanThree{ [x](){ return x > 3; } }; 
    return 0; 
} 

जब मैं try to compile this, मैं निम्नलिखित संकलन त्रुटि मिलती है पर विचार करें इससे बाहर निकलना यह है कि लैम्ब्डा को constexpr के रूप में नहीं माना जा सकता है, इसलिए मैं इसे फ़ंक्शन पॉइंटर के रूप में पास नहीं कर सकता हूं? मैंने x कॉन्स भी बनाने की कोशिश की है, लेकिन यह मदद नहीं करता है।

+20

लैम्ब्डा केवल पॉइंटर को कार्य करने का क्षय कर सकता है अगर वे कुछ भी कैप्चर नहीं करते हैं। – Jarod42

+3

http://blogs.msdn.com/b/oldnewthing/archive/2015/02/20/10594680.aspx – BoBTFish

उत्तर

118

एक लैम्ब्डा केवल एक समारोह सूचक में बदला जा सकता है अगर यह कब्जा नहीं है draft C++11 standard अनुभाग 5.1.2[expr.prim.lambda] कहते हैं (जोर मेरा) से:

The closure type for a lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function having the same parameter and return types as the closure type’s function call operator. The value returned by this conversion function shall be the address of a function that, when invoked, has the same effect as invoking the closure type’s function call operator.

नोट , cppreference भी Lambda functions पर अपने अनुभाग में इसे शामिल करता है।

तो निम्न विकल्पों में काम करेगा:

typedef bool(*DecisionFn)(int); 

Decide greaterThanThree{ [](int x){ return x > 3; } }; 

और इतने होगा इस:

typedef bool(*DecisionFn)(); 

Decide greaterThanThree{ [](){ return true ; } }; 

और के रूप में 5gon12eder अंक बाहर, आप भी std::function उपयोग कर सकते हैं, लेकिन ध्यान रखें कि std::function is heavy weight, तो यह है एक लागत कम व्यापार बंद नहीं है।

54

Shafik Yaghmour's answer सही ढंग से बताता है कि लैम्बडा को फ़ंक्शन पॉइंटर के रूप में क्यों पारित नहीं किया जा सकता है यदि उसके पास कैप्चर होता है। मैं समस्या के लिए दो सरल फिक्स दिखाना चाहता हूं।

  1. उपयोग std::function बजाय कच्चे समारोह संकेत दिए गए।

    यह एक बहुत ही साफ समाधान है। ध्यान दें कि इसमें टाइप एरर (शायद वर्चुअल फ़ंक्शन कॉल) के लिए कुछ अतिरिक्त ओवरहेड शामिल हैं।

    #include <functional> 
    #include <utility> 
    
    struct Decide 
    { 
        using DecisionFn = std::function<bool()>; 
        Decide(DecisionFn dec) : dec_ {std::move(dec)} {} 
        DecisionFn dec_; 
    }; 
    
    int 
    main() 
    { 
        int x = 5; 
        Decide greaterThanThree { [x](){ return x > 3; } }; 
    } 
    
  2. एक लैम्ब्डा अभिव्यक्ति है कि कुछ भी कब्जा नहीं है का उपयोग करें।

    चूंकि आपकी भविष्यवाणी वास्तव में केवल एक बुलियन स्थिर है, इसलिए निम्नलिखित वर्तमान समस्या के आसपास जल्दी से काम करेंगे। एक अच्छा स्पष्टीकरण के लिए this answer देखें क्यों और यह कैसे काम कर रहा है।

    // Your 'Decide' class as in your post. 
    
    int 
    main() 
    { 
        int x = 5; 
        Decide greaterThanThree { 
        (x > 3) ? [](){ return true; } : [](){ return false; } 
        }; 
    } 
    
+7

मुझे आश्चर्य है कि सशर्त अभिव्यक्ति वास्तव में काम करती है ... –

+4

@ टी.सी. यह [प्रश्न] देखें (http://stackoverflow.com/q/27989031) विवरण के लिए यह क्यों काम करता है –

+0

@ShafikYaghmour हाँ, मुझे पता है। मुझे यह सत्यापित करने के लिए [over.built]/p25 जांचना था कि फ़ंक्शन पॉइंटर्स के लिए अंतर्निहित उम्मीदवार है। –

0

के रूप में यह दूसरों के द्वारा उल्लेख किया गया था आप लैम्ब्डा समारोह के बजाय समारोह सूचक स्थानापन्न कर सकते हैं। मैं अपने सी ++ इंटरफ़ेस में F77 ODE सॉल्वर RKSUITE में इस विधि का उपयोग कर रहा हूं।

//C interface to Fortran subroutine UT 
extern "C" void UT(void(*)(double*,double*,double*),double*,double*,double*, 
double*,double*,double*,int*); 

// C++ wrapper which calls extern "C" void UT routine 
static void rk_ut(void(*)(double*,double*,double*),double*,double*,double*, 
double*,double*,double*,int*); 

// Call of rk_ut with lambda passed instead of function pointer to derivative 
// routine 
mathlib::RungeKuttaSolver::rk_ut([](double* T,double* Y,double* YP)->void{YP[0]=Y[1]; YP[1]= -Y[0];}, TWANT,T,Y,YP,YMAX,WORK,UFLAG); 
15

मैं इस एक छोटा सा पुराना पता ..

लेकिन मैं जोड़ना चाहते थे:

लैम्ब्डा अभिव्यक्ति (यहां तक ​​कि कब्जा वाले) एक समारोह सूचक के रूप में संभाला जा सकता है!

यह मुश्किल है क्योंकि एक लैम्ब्डा अभिव्यक्ति एक साधारण कार्य नहीं है। यह वास्तव में एक ऑपरेटर() के साथ एक वस्तु है।

जब आप रचनात्मक होते हैं, तो आप इसका उपयोग कर सकते हैं! std :: function की शैली में "फ़ंक्शन" कक्षा के बारे में सोचें। यदि आप ऑब्जेक्ट को सहेजते हैं!

आप फ़ंक्शन पॉइंटर का भी उपयोग कर सकते हैं।

समारोह सूचक का उपयोग करने के लिए, आप निम्नलिखित का उपयोग कर सकते हैं:

int first = 5; 
auto lambda = [=](int x, int z) { 
    return x + z + first; 
}; 
int(decltype(lambda)::*ptr)(int, int)const = &decltype(lambda)::operator(); 
std::cout << "test = " << (lambda.*ptr)(2, 3) << std::endl; 

एक वर्ग है कि एक समान कार्य करना प्रारंभ कर सकते हैं का निर्माण करने के लिए "std :: समारोह" मैं सिर्फ कम उदाहरण करेंगे।() निष्पादित सबसे पहले आप कर सकते हैं की दुकान वस्तु और समारोह सूचक की तुलना में एक वर्ग/struct की जरूरत भी आप एक ऑपरेटर की जरूरत:

// OT => Object Type 
// RT => Return Type 
// A ... => Arguments 
template<typename OT, typename RT, typename ... A> 
struct lambda_expression { 
    OT _object; 
    RT(OT::*_function)(A...)const; 

    lambda_expression(const OT & object) 
     : _object(object), _function(&decltype(_object)::operator()) {} 

    RT operator() (A ... args) const { 
     return (_object.*_function)(args...); 
    } 
}; 

इस के साथ अब आप पर कब्जा कर लिया चला सकते हैं, noncaptured lambdas, जैसा कि आप मूल का उपयोग कर रहे :

auto capture_lambda() { 
    int first = 5; 
    auto lambda = [=](int x, int z) { 
     return x + z + first; 
    }; 
    return lambda_expression<decltype(lambda), int, int, int>(lambda); 
} 

auto noncapture_lambda() { 
    auto lambda = [](int x, int z) { 
     return x + z; 
    }; 
    return lambda_expression<decltype(lambda), int, int, int>(lambda); 
} 

void refcapture_lambda() { 
    int test; 
    auto lambda = [&](int x, int z) { 
     test = x + z; 
    }; 
    lambda_expression<decltype(lambda), void, int, int>f(lambda); 
    f(2, 3); 

    std::cout << "test value = " << test << std::endl; 
} 

int main(int argc, char **argv) { 
    auto f_capture = capture_lambda(); 
    auto f_noncapture = noncapture_lambda(); 

    std::cout << "main test = " << f_capture(2, 3) << std::endl; 
    std::cout << "main test = " << f_noncapture(2, 3) << std::endl; 

    refcapture_lambda(); 

    system("PAUSE"); 
    return 0; 
} 

इस कोड VS2015 साथ काम करता है आशा है कि यह मदद करता है:)

स्वागत करती है!

संपादित करें: हटा सुइयों टेम्पलेट एफपी, हटाया समारोह सूचक पैरामीटर, lambda_expression को

अद्यतन 04.07.17 का नाम बदला:

template <typename CT, typename ... A> struct function 
: public function<decltype(&CT::operator())(A...)> {}; 

template <typename C> struct function<C> { 
private: 
    C mObject; 

public: 
    function(const C & obj) 
     : mObject(obj) {} 

    template<typename... Args> typename 
    std::result_of<C(Args...)>::type operator()(Args... a) { 
     return this->mObject.operator()(a...); 
    } 

    template<typename... Args> typename 
    std::result_of<const C(Args...)>::type operator()(Args... a) const { 
     return this->mObject.operator()(a...); 
    } 
}; 

namespace make { 
    template<typename C> auto function(const C & obj) { 
     return ::function<C>(obj); 
    } 
} 

int main(int argc, char ** argv) { 
    auto func = make::function([](int y, int x) { return x*y; }); 
    std::cout << func(2, 4) << std::endl; 
    system("PAUSE"); 
    return 0; 
} 
+0

वाह यह अद्भुत है! तो हम एक रैपर वर्ग में संग्रहीत लैम्बडास का आह्वान करने के लिए लैम्ब्डा के कक्षा के आंतरिक पॉइंटर्स (सदस्य फ़ंक्शन ऑपरेटर()) का उपयोग कर सकते हैं !! गजब का!! हमें तब भी std :: फ़ंक्शन की आवश्यकता क्यों है? और क्या lambda_expression को स्वचालित रूप से पास किए गए लैम्ब्डा से सीधे "इन" int "पैरामीटर को कम करने के लिए संभव है? – barney

+1

मैंने अपने कोड का एक छोटा संस्करण जोड़ा है। यह एक साधारण ऑटो f = make :: function (lambda) के साथ काम करना चाहिए; लेकिन मैं काफी शर्मिंदा हूं कि आपको बहुत सारी स्थिति मिल जाएगी, मेरा कोड काम नहीं करेगा। std :: फ़ंक्शन इस तरह से अधिक अच्छी तरह से बनाया गया है और जब आप काम कर रहे हों तो जाना चाहिए। यह यहां शिक्षा और व्यक्तिगत उपयोग के लिए है। – Noxxer

+0

बहुत उत्सुक! दिलचस्प बात यह है कि यह कैसे पार्स करता है? अभिव्यक्ति "सी (Args ...)" कन्स्ट्रक्टर टेम्पलेट पैरामीटर पैक ले रहा है? इसके परिणामस्वरूप इसका परिणाम "ऑपरेटर()" के लिए वैध रिटर्न क्यों है, जहां तक ​​मुझे पता है कि निर्माता किसी भी मूल्य को वापस नहीं करते हैं, तो यह "परिणाम_of" ऑपरेटर() के रिटर्न प्रकार को सी प्रकार से कैसे हटाता है?)) – barney

1

कैप्चरिंग lambdas, समारोह संकेत में परिवर्तित नहीं किया जा सकता है के रूप में this answer बताया।

हालांकि, अक्सर एपीआई को एक फ़ंक्शन पॉइंटर प्रदान करने में काफी दर्द होता है जो केवल एक को स्वीकार करता है। ऐसा करने के लिए सबसे अधिक उद्धृत विधि एक समारोह प्रदान करना और इसके साथ एक स्थिर वस्तु को कॉल करना है।

static Callable callable; 
static bool wrapper() 
{ 
    return callable(); 
} 

यह कठिन है। हम इस विचार को आगे लेते हैं और wrapper बनाने की प्रक्रिया को स्वचालित करते हैं और जीवन को अधिक आसान बनाते हैं।

#include<type_traits> 
#include<utility> 

template<typename Callable> 
union storage 
{ 
    storage() {} 
    std::decay_t<Callable> callable; 
}; 

template<int, typename Callable, typename Ret, typename... Args> 
auto fnptr_(Callable&& c, Ret (*)(Args...)) 
{ 
    static bool used = false; 
    static storage<Callable> s; 
    using type = decltype(s.callable); 

    if(used) 
     s.callable.~type(); 
    new (&s.callable) type(std::forward<Callable>(c)); 
    used = true; 

    return [](Args... args) -> Ret { 
     return Ret(s.callable(args...)); 
    }; 
} 

template<typename Fn, int N = 0, typename Callable> 
Fn* fnptr(Callable&& c) 
{ 
    return fnptr_<N>(std::forward<Callable>(c), (Fn*)nullptr); 
} 

और जैसा कि

void foo(void (*fn)()) 
{ 
    fn(); 
} 

int main() 
{ 
    int i = 42; 
    auto fn = fnptr<void()>([i]{std::cout << i;}); 
    foo(fn); // compiles! 
} 

Live

इसका इस्तेमाल यह अनिवार्य रूप से fnptr से प्रत्येक घटना पर एक गुमनाम समारोह की घोषणा कर रहा है।

ध्यान दें कि fnptr के आमंत्रण पहले लिखित callable उसी कॉल के दिए गए कॉलबेल को ओवरराइट करते हैं।हम int पैरामीटर N के साथ, एक निश्चित डिग्री के लिए इसका समाधान करते हैं।

std::function<void()> func1, func2; 
auto fn1 = fnptr<void(), 1>(func1); 
auto fn2 = fnptr<void(), 2>(func2); // different function 

यदि आपके पास पर्याप्त पागल कर रहे हैं, N उपयोग कम करने के लिए आगे भी एक constexpr counter को डिफ़ॉल्ट बनाया जा सकता।

संबंधित मुद्दे