2012-06-17 11 views
17

द्वारा सी ++ लैम्ब्डा को इंस्टेंटेट करना मैं फ़ंक्शन से फ़ंक्शन बनाने का एक तरीका चाहता हूं। अब मैं लैम्ब्डा फ़ंक्शन द्वारा फ़ंक्शन कॉल को लपेटने की कोशिश करता हूं और बाद में इसे तुरंत चालू करता हूं। लेकिन लैम्ब्डा कन्स्ट्रक्टर की तुलना में संकलक कहता है। तो क्या इस कोड को संकलित करने का कोई तरीका है? या शायद इसके लिए एक और तरीका?इसके प्रकार

#include <iostream> 

void func() 
{ 
    std::cout << "Hello"; 
} 

auto t = []{ func(); }; 
typedef decltype(t) functor_type; 

template <class F> 
void functor_caller() 
{ 
    F f; 
    f(); 
} 

int main() 
{ 
    functor_caller<functor_type>(); 
    return 0; 
} 

अब मैं इस तरह के संकलक त्रुटि मिलती है: एक ही तरीका है मेरी राय में

error: use of deleted function '<lambda()>::<lambda>()' 

error: a lambda closure type has a deleted default constructor 

मैक्रो का उपयोग करने के लिए है:

#define WRAP_FUNC(f) \ 
struct f##_functor  \ 
{      \ 
    template <class... Args >        \ 
    auto operator()(Args ... args) ->decltype(f(args...)) \ 
    {              \ 
     return f(args...);        \ 
    }              \ 
}; 

तो

WRAP_FUNC(func); 

और उसके बाद (मुख्य में)

functor_caller<func_functor>() 
+0

मैं 'main' में 'functor_type' प्रकार का एक चर घोषित नहीं कर सकता। पहले काम करने के लिए इसे पाने की जरूरत है। – jxh

+1

@ user315052: यह वास्तव में एक ही समस्या है। – Nawaz

+0

क्या आपने 'एफ एफ (टी);'? वर्तमान में, 'functor_caller' को' t' के बारे में पता नहीं है। – MSalters

उत्तर

2

सं

हालांकि मुझे विश्वास है कि lambdas कॉपी किया जा सकता है, तो आपके functor_caller इसके विशेषता प्रारंभ करने में एक तर्क ले सकता है।

फिर भी, पहिया को पुनर्निर्मित करने के बजाय, मैं इसके बजाय std::function का उपयोग करूंगा।

+0

का उत्तर है क्या आप std :: function का उपयोग करने का एक उदाहरण दिखा सकते हैं? – Andigor

+0

सं। मेरे प्रश्न के संदर्भ में std :: function का उपयोग करने का उदाहरण। – Andigor

+3

@Andigor: मैं अंत में इस मुद्दे को समझ गया। लैम्ब्डा का * शरीर * इसके प्रकार का हिस्सा नहीं है। इसलिए, डिफ़ॉल्ट रूप से लैम्ब्डा को इसके प्रकार से बनाते हुए, भले ही यह संभव हो, समझ में नहीं आता। यह डिफ़ॉल्ट रूप से 'शून्य (*)() 'का निर्माण करने जैसा है, आपको एक हाइप पॉइंटर एक हाइपोटेटिक फ़ंक्शन है जो कोई पैरामीटर नहीं लेता है और कुछ भी नहीं देता है => इस शून्य सूचक के साथ आप कुछ भी सार्थक नहीं कर सकते हैं! यही कारण है कि आपको मौजूदा लैम्ब्डा * प्रतिलिपि * की आवश्यकता है। –

8

कोड समझ में नहीं आता है। कल्पना कीजिए कि आप इस तरह से एक पर कब्जा करने लैम्ब्डा है:

{ 
    int n = 0; 
    auto t = [&n](int a) -> int { return n += a; }; 
} 

क्या यह संभवतः प्रकार decltype(t) की एक वस्तु डिफ़ॉल्ट-निर्माण करने के लिए अर्थ हो सकता है?

@Matthieu पता चलता है, तो आप एक function वस्तु में लैम्ब्डा लपेट सकता है:

std::function<int(int)> F = t; 

या आप लैम्ब्डा के प्रकार (या किसी भी प्रतिदेय इकाई) पर सीधे अपने कॉल-साइट टेम्पलेट सकता है:

template <typename F> 
int compute(int a, int b, F f) 
{ 
    return a * f(b); // example 
} 

उपयोग: int a = 0; for (int i : { 1, 3, 5 }) { a += compute(10, i, t); }

सभी संभव पर है, तो दूसरा शैली बेहतर है,करने के लिए रूपांतरण के बाद से 210 एक गैर-तुच्छ, संभावित रूप से महंगा संचालन है, जैसा कि परिणामी वस्तु के माध्यम से वास्तविक कार्य कॉल है। हालांकि, अगर आपको विषम कॉल करने योग्य इकाइयों का एक समान संग्रह स्टोर करने की आवश्यकता है, तो std::function सबसे आसान और सबसे सुविधाजनक समाधान हो सकता है।

+0

आपके उदाहरण में लैम्बडा के सैद्धांतिक रूप से डिफ़ॉल्ट निर्माण का अर्थ है कि मुझे 'n' का संदर्भ मिलता है जो उस ऑब्जेक्ट को नष्ट कर दिया गया था। लेकिन अगर 'n' रहता है तो मुझे सही संदर्भ मिला। – Andigor

+0

लेकिन मेरा सवाल संकलन समय पर फ़ंक्शन से फ़ंक्शन बनाने के बारे में था ... – Andigor

+0

@Andigor: यदि आप templaty शैली का उपयोग करते हैं, तो आपको एक मध्यवर्ती मज़ेदार बनाने की भी आवश्यकता नहीं है - आप केवल फ़ंक्शन (पॉइंटर) को सीधे पास कर सकते हैं ... और ध्यान दें कि 'n' दायरे में आवश्यक नहीं है, न ही यह अर्थपूर्ण रूप से इस प्रकार का हिस्सा है। –

6

हम मान सकते हैं कि लैम्ब्डा हमेशा खाली रहेगा, इस प्रकार, हम केवल एक और खाली प्रकार से कास्ट कर सकते हैं, क्योंकि दोनों के पास एक ही मेमोरी लेआउट है।

template<class F> 
struct wrapper 
{ 
    static_assert(std::is_empty<F>(), "Lambdas must be empty"); 
    template<class... Ts> 
    auto operator()(Ts&&... xs) const -> decltype(reinterpret_cast<const F&>(*this)(std::forward<Ts>(xs)...)) 
    { 
     return reinterpret_cast<const F&>(*this)(std::forward<Ts>(xs)...); 
    } 
}; 

अब एक स्थिर ज़ोर सुनिश्चित करें कि लैम्ब्डा हमेशा खाली है बनाने के लिए जोड़ा जाता है: तो हम एक आवरण वर्ग कि एक डिफ़ॉल्ट constructible समारोह वस्तु बना देता है निर्माण। यह हमेशा मामला होना चाहिए (क्योंकि इसे फ़ंक्शन पॉइंटर को क्षय करने की आवश्यकता होती है), लेकिन मानक स्पष्ट रूप से इसकी गारंटी नहीं देता है। इसलिए हम कम से कम लैम्बडा के पागल कार्यान्वयन को पकड़ने के लिए जोर देते हैं। फिर हमने रैपर वर्ग को लैम्ब्डा में डाला क्योंकि दोनों खाली हैं।

अंत में लैम्ब्डा इस तरह का निर्माण किया जा सकता है:

template <class F> 
void function_caller() 
{ 
    wrapper<F> f; 
    f(); 
} 
+0

अच्छा समाधान। बस थोड़ा सा संशोधन: '* reinterpret_cast (nullptr) (...) 'रनरटाइम पर पहुंच उल्लंघन का कारण बनता है जब भी' एफ :: ऑपरेटर() 'रैपर' ऑब्जेक्ट के बाद चुपचाप स्मृति को दूषित करने के बजाय खुद को अव्यवस्थित करने की कोशिश करता है। 'static_assert() 'हमें आश्वासन देता है कि' एफ 'में कोई फ़ील्ड नहीं है, लेकिन ... आप कभी नहीं जानते कि' एफ' क्या करता है। –

+0

एक शून्य संकेतक को संदर्भित करने के लिए एक प्रवेश उल्लंघन की गारंटी नहीं है। –

8

lambdas डिफ़ॉल्ट कंस्ट्रक्टर्स जरूरत नहीं है। कभी। एकमात्र रचनाकार जिन्हें वे कभी-कभी पहुंच देते हैं (वे जो भी पकड़ते हैं उसके आधार पर) प्रतिलिपि और/या कन्स्ट्रक्टर को स्थानांतरित करते हैं।

यदि आप कोई सार्वजनिक डिफ़ॉल्ट कन्स्ट्रक्टर वाला कोई फ़ैक्टर नहीं बनाते हैं, तो आपको एक ही त्रुटि मिल जाएगी।

सी ++ में 17 आप इसे constexpr लैम्ब्डा और operator+ से क्षय-टू-फ़ंक्शन पॉइंटर के साथ हल कर सकते हैं। एक प्रकार जो फ़ंक्शन पॉइंटर लेता है और उसे आमंत्रित करता है auto टेम्पलेट पैरामीटर के साथ आसान है।

सी ++ में आपको थोड़ा हैकी मिलना होगा।

template<class F> 
struct stateless_lambda_t { 
    static std::aligned_storage_t< sizeof(F), alignof(F) >& data() { 
    static std::aligned_storage_t< sizeof(F), alignof(F) > retval; 
    return retval; 
    }; 
    template<class Fin, 
    std::enable_if_t< !std::is_same< std::decay_t<Fin>, stateless_lambda_t >{}, int> =0 
    > 
    stateless_lambda_t(Fin&& f) { 
    new ((void*)&data()) F(std::forward<Fin>(f)); 
    } 
    stateless_lambda_t(stateless_lambda_t const&)=default; 
    template<class...Args> 
    decltype(auto) operator()(Args&&...args)const { 
    return (*static_cast<F*>((void*)&data()))(std::forward<Args>(args)...); 
    } 
    stateless_lambda_t() = default; 
}; 
template<class F> 
stateless_lambda_t<std::decay_t<F>> make_stateless(F&& fin) { 
    return {std::forward<F>(fin)}; 
} 

अब हम कर सकते हैं:

auto t = make_stateless([]{ func(); }); 

और अपने कोड काम करता है।

static_assert या SFINAE कि F वास्तव में एक खाली प्रकार एक अच्छा विचार हो सकता है। आप गुणवत्ता के लिए जानते हैं।

सी ++ 14 सुविधाओं का उपयोग मैन्युअल decltype और typename और ::type कीवर्ड के साथ प्रतिस्थापित किया जा सकता है। यह उत्तर मूल रूप से एक C++ 14 प्रश्न के लिए लिखा गया था जिसे इस के डुप्लिकेट के रूप में बंद कर दिया गया था।

live example

+0

मुझे डर है कि मैं इसे काफी काम नहीं कर सकता। क्या आप एक पूर्ण कोड उदाहरण पोस्ट कर सकते हैं? – Knarf

+1

@Knarf संकलन त्रुटियों को ठीक किया गया, लाइव उदाहरण पोस्ट किया गया। – Yakk

+0

यह काम किया! धन्यवाद! – Knarf

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