खोज…


वाक्य - विन्यास

  • [ डिफ़ॉल्ट-कैप्चर , कैप्चर-लिस्ट ] ( तर्क-सूची ) परस्पर फेंक-विनिर्देश विशेषताएँ -> वापसी-प्रकार { लैम्ब्डा-बॉडी } // // लैम्ब्डा स्पेसियर्स और विशेषताओं का क्रम।
  • [ कैप्चर-लिस्ट ] ( तर्क-सूची ) { lambda-body } // कॉमन लैम्ब्डा परिभाषा।
  • [=] ( तर्क-सूची ) { लैम्ब्डा-बॉडी } // // सभी आवश्यक स्थानीय वेरिएबल्स को मूल्य द्वारा कैप्चर करता है।
  • [और] ( तर्क-सूची ) { लंबोदर-निकाय } // संदर्भ द्वारा सभी आवश्यक स्थानीय चरों पर कब्जा।
  • [ कैप्चर-लिस्ट ] { लैम्ब्डा-बॉडी } // आर्ग्युमेंट लिस्ट और स्पेसिफिकेशन्स को छोड़ा जा सकता है।

पैरामीटर

पैरामीटर विवरण
डिफ़ॉल्ट पर कब्जा निर्दिष्ट करता है कि सभी गैर-सूचीबद्ध चर कैसे कैप्चर किए जाते हैं। हो सकता है = (मूल्य द्वारा कब्जा) या & (संदर्भ द्वारा कब्जा)। यदि छोड़ा गया है, तो गैर-सूचीबद्ध चर लंबोदर-निकाय के भीतर दुर्गम हैं। डिफ़ॉल्ट-कैप्चर को कैप्चर-सूची से पहले होना चाहिए।
कब्जा-सूची निर्दिष्ट करता है कि लैम्ब्डा-निकाय के भीतर स्थानीय चर कैसे सुलभ किए जाते हैं। बिना उपसर्ग के चर को मूल्य द्वारा पकड़ लिया जाता है। संदर्भों के साथ उपसर्ग किए गए & संदर्भ द्वारा पकड़े गए। एक वर्ग विधि के भीतर, this उपयोग इसके सभी सदस्यों को संदर्भ द्वारा सुलभ बनाने के लिए किया जा सकता है। गैर-सूचीबद्ध चर अप्राप्य हैं, जब तक कि सूची डिफ़ॉल्ट-कैप्चर से पहले न हो।
तर्क-सूची लैम्बडा फ़ंक्शन के तर्कों को निर्दिष्ट करता है।
परिवर्तनशील (वैकल्पिक) आम तौर पर मूल्य द्वारा कब्जा किए गए चर constmutable निर्दिष्ट करना उन्हें गैर- mutable बनाता है। कॉल के बीच उन चरों को बदला जाता है।
थ्रो-विनिर्देश (वैकल्पिक) लैम्बडा फ़ंक्शन के अपवाद फेंकने वाले व्यवहार को निर्दिष्ट करता है। उदाहरण के लिए: noexcept या throw(std::exception)
गुण (वैकल्पिक) लंबो फ़ंक्शन के लिए कोई विशेषता। उदाहरण के लिए, यदि लैम्ब्डा-बॉडी हमेशा एक अपवाद फेंकता है तो [[noreturn]] का उपयोग किया जा सकता है।
-> रिटर्न-टाइप (वैकल्पिक) लैम्बडा फ़ंक्शन के रिटर्न प्रकार को निर्दिष्ट करता है। आवश्यक जब संकलक द्वारा रिटर्न प्रकार निर्धारित नहीं किया जा सकता है।
लैम्ब्डा शरीर एक कोड ब्लॉक जिसमें लैम्बडा फ़ंक्शन का कार्यान्वयन होता है।

टिप्पणियों

C ++ 17 (वर्तमान ड्राफ्ट) constexpr लैम्ब्डा को प्रस्तुत constexpr , मूल रूप से लैम्ब्डा है जिसका संकलन समय पर मूल्यांकन किया जा सकता है। एक लैम्ब्डा स्वचालित रूप से constexpr यदि यह constexpr आवश्यकताओं को पूरा करता है, लेकिन आप इसे constexpr कीवर्ड का उपयोग करके भी निर्दिष्ट कर सकते हैं:

//Explicitly define this lambdas as constexpr
[]() constexpr {
    //Do stuff
}

एक लंबोदर अभिव्यक्ति क्या है?

एक लंबोदर अभिव्यक्ति सरल फ़ंक्शन ऑब्जेक्ट बनाने के लिए एक संक्षिप्त तरीका प्रदान करती है। एक लंबोदर अभिव्यक्ति एक प्रचलन है जिसके परिणाम ऑब्जेक्ट को क्लोजर ऑब्जेक्ट कहा जाता है, जो एक फ़ंक्शन ऑब्जेक्ट की तरह व्यवहार करता है।

'लैम्ब्डा एक्सप्रेशन' नाम की उत्पत्ति लैम्ब्डा कैलकुलस से हुई है , जो 1930 के दशक में अलोंजो चर्च द्वारा आविष्कार की गई एक गणितीय औपचारिकता है जो तर्क और कम्प्यूटेबिलिटी के बारे में सवालों की जांच करती है। लैम्ब्डा कैलकुलस ने एक कार्यात्मक प्रोग्रामिंग भाषा, LISP के आधार का गठन किया। लैम्ब्डा कैलकुलस और LISP की तुलना में, C ++ लैंबडा एक्सप्रेशंस में अनाम के गुणों को साझा किया जाता है, और आसपास के संदर्भ से वेरिएबल्स को कैप्चर किया जाता है, लेकिन उनके पास काम करने और वापस लौटने की क्षमता होती है।

एक लंबोदर अभिव्यक्ति का उपयोग अक्सर उन कार्यों के तर्क के रूप में किया जाता है जो एक कॉल करने योग्य वस्तु लेते हैं। नामांकित फ़ंक्शन बनाने की तुलना में यह सरल हो सकता है, जिसका उपयोग केवल तर्क के रूप में पारित होने पर किया जाएगा। ऐसे मामलों में, लैम्ब्डा अभिव्यक्ति आमतौर पर पसंद की जाती है क्योंकि वे फ़ंक्शन ऑब्जेक्ट को इनलाइन को परिभाषित करने की अनुमति देते हैं।

एक लैम्ब्डा में आमतौर पर तीन भाग होते हैं: एक कैप्चर सूची [] , एक वैकल्पिक पैरामीटर सूची () और एक बॉडी {} , जो सभी खाली हो सकती हैं:

[](){}                // An empty lambda, which does and returns nothing

सूची पर कब्जा

[] कब्जा सूची है । डिफ़ॉल्ट रूप से, एन्कोडिंग स्कोप के चर को लैम्ब्डा द्वारा एक्सेस नहीं किया जा सकता है। एक वैरिएबल को कैप्चर करना इसे लैम्बडा के अंदर या तो कॉपी के रूप में या संदर्भ के रूप में सुलभ बनाता है। कैप्टिव चर लैम्ब्डा का एक हिस्सा बन जाते हैं; कार्य तर्कों के विपरीत, लैम्बडा को कॉल करते समय उन्हें पारित नहीं करना पड़ता है।

int a = 0;                       // Define an integer variable
auto f = []()   { return a*9; }; // Error: 'a' cannot be accessed
auto f = [a]()  { return a*9; }; // OK, 'a' is "captured" by value
auto f = [&a]() { return a++; }; // OK, 'a' is "captured" by reference
                                 //      Note: It is the responsibility of the programmer
                                 //      to ensure that a is not destroyed before the
                                 //      lambda is called.
auto b = f();                    // Call the lambda function. a is taken from the capture list and not passed here.

पैरामीटर सूची

() पैरामीटर सूची है , जो लगभग नियमित कार्यों के समान है। यदि लैम्ब्डा कोई तर्क नहीं देता है, तो इन कोष्ठकों को छोड़ा जा सकता है (सिवाय इसके कि आपको लैम्ब्डा mutable घोषित करने की आवश्यकता है)। ये दो लंबोदर समतुल्य हैं:

auto call_foo  = [x](){ x.foo(); };
auto call_foo2 = [x]{ x.foo(); };
सी ++ 14

पैरामीटर सूची वास्तविक प्रकार के बजाय प्लेसहोल्डर प्रकार के auto उपयोग कर सकती है। ऐसा करने से, यह तर्क एक फ़ंक्शन टेम्पलेट के टेम्पलेट पैरामीटर की तरह व्यवहार करता है। जब आप जेनेरिक कोड में वेक्टर को क्रमबद्ध करना चाहते हैं, तो लैम्ब्डा निम्नलिखित हैं:

auto sort_cpp11 = [](std::vector<T>::const_reference lhs, std::vector<T>::const_reference rhs) { return lhs < rhs; }; 
auto sort_cpp14 = [](const auto &lhs, const auto &rhs) { return lhs < rhs; }; 

शरीर की क्रिया

{} शरीर है , जो नियमित कार्यों के समान है।


एक लंबोदर कहलाता है

एक लंबोदर अभिव्यक्ति का परिणाम ऑब्जेक्ट एक क्लोजर है , जिसे operator() रूप में उपयोग किया जा सकता है (अन्य फ़ंक्शन ऑब्जेक्ट्स के साथ):

int multiplier = 5;
auto timesFive = [multiplier](int a) { return a * multiplier; }; 
std::out << timesFive(2); // Prints 10

multiplier = 15;
std::out << timesFive(2); // Still prints 2*5 == 10

वापसी प्रकार

डिफ़ॉल्ट रूप से, लैम्बडा एक्सप्रेशन का रिटर्न प्रकार काटा जाता है।

[](){ return true; };

इस मामले में वापसी प्रकार bool

आप निम्न सिंटैक्स का उपयोग करके मैन्युअल रूप से रिटर्न प्रकार भी निर्दिष्ट कर सकते हैं:

[]() -> bool { return true; };

म्यूटेबल लाम्बा

लैम्ब्डा में मूल्य द्वारा कैप्चर की गई वस्तुएँ डिफ़ॉल्ट रूप से अपरिवर्तनीय होती हैं। ऐसा इसलिए है क्योंकि उत्पन्न क्लोज़र ऑब्जेक्ट का operator() डिफ़ॉल्ट रूप से const है।

auto func = [c = 0](){++c; std::cout << c;};  // fails to compile because ++c
                                              // tries to mutate the state of
                                              // the lambda.

कीवर्ड mutable का उपयोग करके संशोधन की अनुमति दी जा सकती है, जो करीब ऑब्जेक्ट के operator() गैर- const बनाते हैं:

auto func = [c = 0]() mutable {++c; std::cout << c;};

यदि रिटर्न प्रकार के साथ एक साथ उपयोग किया जाता है, तो इसके पहले mutable आता है।

auto func = [c = 0]() mutable -> int {++c; std::cout << c; return c;};

लंबोदर की उपयोगिता का उदाहरण देने के लिए

C ++ 11 से पहले:

सी ++ 11
// Generic functor used for comparison
struct islessthan
{
    islessthan(int threshold) : _threshold(threshold) {}

    bool operator()(int value) const
    {
        return value < _threshold;
    }
private:
    int _threshold;
};

// Declare a vector
const int arr[] = { 1, 2, 3, 4, 5 };
std::vector<int> vec(arr, arr+5);

// Find a number that's less than a given input (assume this would have been function input)
int threshold = 10;
std::vector<int>::iterator it = std::find_if(vec.begin(), vec.end(), islessthan(threshold));

C ++ 11 के बाद से:

सी ++ 11
// Declare a vector
std::vector<int> vec{ 1, 2, 3, 4, 5 };

// Find a number that's less than a given input (assume this would have been function input)
int threshold = 10;
auto it = std::find_if(vec.begin(), vec.end(), [threshold](int value) { return value < threshold; });

रिटर्न प्रकार निर्दिष्ट करना

एकल रिटर्न स्टेटमेंट, या कई रिटर्न स्टेटमेंट वाले लैम्ब्डा

// Returns bool, because "value > 10" is a comparison which yields a Boolean result
auto l = [](int value) {
    return value > 10;
}

विभिन्न प्रकारों के कई रिटर्न स्टेटमेंट वाले लैम्ब्डा के लिए, कंपाइलर रिटर्न प्रकार नहीं घटा सकता है:

// error: return types must match if lambda has unspecified return type
auto l = [](int value) {
    if (value < 10) {
        return 1;
    } else {
        return 1.5;
    }
};

इस स्थिति में आपको रिटर्न प्रकार स्पष्ट रूप से बताना होगा:

// The return type is specified explicitly as 'double'
auto l = [](int value) -> double {
    if (value < 10) {
        return 1;
    } else {
        return 1.5;
    }
};

इस प्रकार के नियम auto टाइप कटौती के नियमों से मेल खाते हैं। स्पष्ट रूप से निर्दिष्ट रिटर्न प्रकारों के बिना लम्बदा कभी भी संदर्भ नहीं देते हैं, इसलिए यदि कोई संदर्भ प्रकार वांछित है तो इसे स्पष्ट रूप से निर्दिष्ट किया जाना चाहिए:

auto copy = [](X& x) { return x; };       // 'copy' returns an X, so copies its input
auto ref  = [](X& x) -> X& { return x; }; // 'ref' returns an X&, no copy

मूल्य द्वारा कब्जा

यदि आप कैप्चर सूची में चर का नाम निर्दिष्ट करते हैं, तो लैम्ब्डा इसे मूल्य से कैप्चर करेगा। इसका मतलब है कि लैम्ब्डा के लिए उत्पन्न क्लोजर प्रकार चर की एक प्रति संग्रहीत करता है। इसके लिए यह भी आवश्यक है कि चर का प्रकार प्रतिलिपि-निर्माण योग्य हो :

int a = 0;

[a]() {
    return a;   // Ok, 'a' is captured by value
};
सी ++ 14
auto p = std::unique_ptr<T>(...);

[p]() {         // Compile error; `unique_ptr` is not copy-constructible
    return p->createWidget(); 
};

C ++ 14 से, मौके पर वेरिएबल्स को इनिशियलाइज़ करना संभव है। इससे लैम्बडा पर कब्जा करने के लिए केवल प्रकारों को स्थानांतरित किया जा सकता है।

सी ++ 14
auto p = std::make_unique<T>(...);

[p = std::move(p)]() {
    return p->createWidget(); 
};

भले ही एक लैम्ब्डा उनके नाम से दिए जाने पर वेरिएबल्स को वैल्यू द्वारा कैप्चर करता है, लेकिन ऐसे वेरिएबल को लैम्बडा बॉडी के भीतर डिफॉल्ट रूप से संशोधित नहीं किया जा सकता है। इसका कारण यह है कि क्लोजर प्रकार operator() const घोषणा में लैम्बडा बॉडी डालता है।

यह const क्लोजर टाइप के मेंबर वेरिएबल्स में एक्सेस के लिए लागू होता है, और कैप्चर किए गए वैरिएबल पर क्लोजर के सदस्य होते हैं (इसके विपरीत सभी दिखावे):

int a = 0;

[a]() {
    a = 2;      // Illegal, 'a' is accessed via `const`

    decltype(a) a1 = 1; 
    a1 = 2; // valid: variable 'a1' is not const
};

const को निकालने के लिए, आपको mutable पर मौजूद mutable निर्दिष्ट करना होगा:

int a = 0;

[a]() mutable {
    a = 2;      // OK, 'a' can be modified
    return a;
};

क्योंकि a मूल्य द्वारा कब्जा कर लिया गया था, किसी भी संशोधन लैम्ब्डा फोन करके किया प्रभावित नहीं करेगा a । का मूल्य a , लैम्ब्डा में नकल किया गया था जब यह निर्माण किया गया था इसलिए की लैम्ब्डा के प्रति a बाहरी से अलग है a चर।

int a = 5 ; 
auto plus5Val = [a] (void) { return a + 5 ; } ; 
auto plus5Ref = [&a] (void) {return a + 5 ; } ; 

a = 7 ; 
std::cout << a << ", value " << plus5Val() << ", reference " << plus5Ref() ;
// The result will be "7, value 10, reference 12"

सामान्यीकृत कब्जा

सी ++ 14

लैंबडैस केवल चर के बजाय अभिव्यक्ति पर कब्जा कर सकते हैं। यह लैम्बदास को केवल प्रकार के स्टोर करने की अनुमति देता है:

auto p = std::make_unique<T>(...);

auto lamb = [p = std::move(p)]() //Overrides capture-by-value of `p`.
{
  p->SomeFunc();
};

यह बाहरी p वैरिएबल को लैम्ब्डा कैप्चर वैरिएबल में ले जाता है, जिसे p भी कहा जाता है। lamb अब make_unique द्वारा आवंटित मेमोरी का मालिक है। क्योंकि क्लोजर में एक प्रकार होता है जो गैर-प्रतिलिपि योग्य होता है, इसका मतलब है कि lamb स्वयं गैर-प्रतिलिपि योग्य है। लेकिन इसे स्थानांतरित किया जा सकता है:

auto lamb_copy = lamb; //Illegal
auto lamb_move = std::move(lamb); //legal.

अब lamb_move स्मृति का स्वामी है।


ध्यान दें कि std::function<> आवश्यक है कि संग्रहित मूल्य कॉपी किए जा सकें। आप अपनी स्वयं की चाल-केवल-आवश्यकता वाले std::function को लिख सकते हैं, या आप लैम्ब्डा को एक shared_ptr आवरण में भर सकते हैं:

auto shared_lambda = [](auto&& f){
  return [spf = std::make_shared<std::decay_t<decltype(f)>>(decltype(f)(f))]
  (auto&&...args)->decltype(auto) {
    return (*spf)(decltype(args)(args)...);
  };
};
auto lamb_shared = shared_lambda(std::move(lamb_move));

हमारे कदम-केवल लैम्ब्डा को ले जाता है और अपने राज्य को एक साझा पॉइंटर में भर देता है और फिर एक लैम्ब्डा लौटाता है जिसे कॉपी किया जा सकता है, और फिर एक std::function या समान में संग्रहीत किया जाता है।


सामान्यीकृत कैप्चर वैरिएबल के प्रकार के लिए auto टाइप कटौती का उपयोग करता है। यह डिफ़ॉल्ट रूप से इन कैप्चर को मूल्यों के रूप में घोषित करेगा, लेकिन वे संदर्भ भी हो सकते हैं:

int a = 0;

auto lamb = [&v = a](int add) //Note that `a` and `v` have different names
{
  v += add; //Modifies `a`
};

lamb(20); //`a` becomes 20.

सामान्य कैप्चर को बाहरी चर को पकड़ने की आवश्यकता नहीं है। यह एक मनमाना अभिव्यक्ति पर कब्जा कर सकता है:

auto lamb = [p = std::make_unique<T>(...)]()
{
    p->SomeFunc();
}

यह लंबोदर को मनमाने ढंग से मान देने के लिए उपयोगी है जिसे वे पकड़ कर संभावित रूप से संशोधित कर सकते हैं, बिना उन्हें लंबोदर को बाहरी घोषित करने के लिए। बेशक, यह केवल तभी उपयोगी है जब आप लैम्ब्डा के अपना काम पूरा करने के बाद उन चरों को एक्सेस करने का इरादा नहीं रखते हैं।

संदर्भ द्वारा कैप्चर करें

आप एक साथ एक स्थानीय वेरिएबल का नाम पूर्व में होना तो & , तो चर संदर्भ द्वारा कब्जा कर लिया हो जाएगा। वैचारिक रूप से, इसका मतलब है कि लैम्ब्डा के क्लोजर प्रकार का एक संदर्भ चर होगा, जिसे लैम्बडा के दायरे के बाहर से संबंधित वैरिएबल के संदर्भ के रूप में प्रारंभ किया जाएगा। भेड़ के बच्चे के शरीर में चर का कोई भी उपयोग मूल चर को संदर्भित करेगा:

// Declare variable 'a'
int a = 0;

// Declare a lambda which captures 'a' by reference
auto set = [&a]() {
    a = 1;
};

set();
assert(a == 1);

कीवर्ड mutable की जरूरत नहीं है, क्योंकि खुद a const नहीं है।

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

डिफ़ॉल्ट कब्जा

डिफ़ॉल्ट रूप से, स्थानीय चर जो स्पष्ट रूप से कैप्चर सूची में निर्दिष्ट नहीं हैं, उन्हें लंबोदा निकाय के भीतर से एक्सेस नहीं किया जा सकता है। हालांकि, लैम्ब्डा बॉडी द्वारा नामित चर पर कब्जा करना संभव है:

int a = 1;
int b = 2;

// Default capture by value
[=]() { return a + b; }; // OK; a and b are captured by value

// Default capture by reference
[&]() { return a + b; }; // OK; a and b are captured by reference

स्पष्ट कैप्चरिंग को अभी भी अंतर्निहित डिफ़ॉल्ट कैप्चरिंग के साथ किया जा सकता है। स्पष्ट कैप्चर परिभाषा डिफ़ॉल्ट कैप्चर को ओवरराइड करेगी:

int a = 0;
int b = 1;

[=, &b]() {
    a = 2; // Illegal; 'a' is capture by value, and lambda is not 'mutable'
    b = 2; // OK; 'b' is captured by reference
};

जेनेरिक लम्बदास

c ++ 14

लैम्ब्डा फ़ंक्शन मनमाने प्रकार के तर्क ले सकते हैं। यह एक मेमने को अधिक सामान्य बनाने की अनुमति देता है:

auto twice = [](auto x){ return x+x; };

int i = twice(2); // i == 4
std::string s = twice("hello"); // s == "hellohello"

यह C ++ में क्लोजर प्रकार के operator() एक टेम्प्लेट फ़ंक्शन को अधिभारित करके कार्यान्वित किया जाता है। निम्न प्रकार का लम्बडा क्लोजर के समतुल्य व्यवहार है:

struct _unique_lambda_type
{
  template<typename T>
  auto operator() (T x) const {return x + x;}
};

जेनेरिक लैम्ब्डा में सभी पैरामीटर सामान्य होने की आवश्यकता नहीं है:

[](auto x, int y) {return x + y;}

यहाँ, x को पहले फ़ंक्शन तर्क के आधार पर घटाया जाता है, जबकि y हमेशा int

सामान्य लैंबडा auto और & लिए सामान्य नियमों का उपयोग करते हुए, संदर्भ के अनुसार तर्क ले सकते हैं। एक सामान्य पैरामीटर के रूप में लिया जाता है auto&& , यह एक है अग्रेषण संदर्भ दिया गया तर्क, न कि किसी को rvalue संदर्भ :

auto lamb1 = [](int &&x) {return x + 5;};
auto lamb2 = [](auto &&x) {return x + 5;};
int x = 10;
lamb1(x); // Illegal; must use `std::move(x)` for `int&&` parameters.
lamb2(x); // Legal; the type of `x` is deduced as `int&`.

लैम्ब्डा फ़ंक्शंस परिवर्तनशील हो सकते हैं और अपने तर्कों को पूरी तरह से आगे बढ़ा सकते हैं:

auto lam = [](auto&&... args){return f(std::forward<decltype(args)>(args)...);};

या:

auto lam = [](auto&&... args){return f(decltype(args)(args)...);};

जो केवल auto&& प्रकार के चर के साथ "ठीक से" काम करता है।

जेनेरिक लैम्ब्डा का उपयोग करने का एक मजबूत कारण वाक्यविन्यास पर जाने के लिए है।

boost::variant<int, double> value;
apply_visitor(value, [&](auto&& e){
  std::cout << e;
});

यहां हम बहुरूपिए तरीके से दौरा कर रहे हैं; लेकिन अन्य संदर्भों में, हम जिस प्रकार से गुजर रहे हैं उसका नाम दिलचस्प नहीं है:

mutex_wrapped<std::ostream&> os = std::cout;
os.write([&](auto&& os){
  os << "hello world\n";
});

std::ostream& के प्रकार को दोहराते हुए std::ostream& यहां शोर है; हर बार जब आप इसका उपयोग करते हैं तो यह एक चर के प्रकार का उल्लेख करने जैसा होगा। यहाँ हम एक आगंतुक बना रहे हैं, लेकिन कोई बहुरूपिया नहीं; auto का उपयोग उसी कारण से किया जाता है जिस कारण आप auto का उपयोग किसी for(:) लूप में करते हैं।

फ़ंक्शन पॉइंटर में रूपांतरण

यदि लैम्बडा की कैप्चर सूची खाली है, तो लैम्बडा का फ़ंक्शन पॉइंटर में एक अंतर्निहित रूपांतरण होता है जो समान तर्क लेता है और समान रिटर्न प्रकार देता है:

auto sorter = [](int lhs, int rhs) -> bool {return lhs < rhs;};

using func_ptr = bool(*)(int, int);
func_ptr sorter_func = sorter; // implicit conversion

इस तरह के रूपांतरण को यूनिरी प्लस ऑपरेटर के उपयोग से भी लागू किया जा सकता है:

func_ptr sorter_func2 = +sorter; // enforce implicit conversion

इस फ़ंक्शन पॉइंटर को कॉल करना लंबोदर पर इनवॉइस operator() तरह व्यवहार करता है। यह फ़ंक्शन पॉइंटर किसी भी तरह से स्रोत लैम्ब्डा क्लोजर के अस्तित्व पर निर्भर नहीं है। इसलिए यह लंबोदर बंद होने की संभावना है।

यह सुविधा मुख्य रूप से एपीआई के साथ लैम्ब्डा का उपयोग करने के लिए उपयोगी है जो सी ++ फ़ंक्शन ऑब्जेक्ट्स के बजाय फ़ंक्शन पॉइंटर्स में सौदा करती है।

सी ++ 14

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

auto sorter = [](auto lhs, auto rhs) { return lhs < rhs; };
using func_ptr = bool(*)(int, int);
func_ptr sorter_func = sorter;  // deduces int, int
// note however that the following is ambiguous
// func_ptr sorter_func2 = +sorter;

वर्ग लंबोदर और इस पर कब्जा

एक वर्ग के सदस्य समारोह में मूल्यांकन किए गए एक लंबोदर अभिव्यक्ति का तात्पर्य उस वर्ग का मित्र है:

class Foo
{
private:
    int i;
    
public:
    Foo(int val) : i(val) {}
    
    // definition of a member function
    void Test()
    {
        auto lamb = [](Foo &foo, int val)
        {
            // modification of a private member variable
            foo.i = val;
        };
        
        // lamb is allowed to access a private member, because it is a friend of Foo
        lamb(*this, 30);
    }
};

इस तरह का मेमना न केवल उस वर्ग का मित्र होता है, उसकी उसी कक्षा तक पहुंच होती है, जिसके पास वह वर्ग होता है।

लैम्ब्डा this पॉइंटर को कैप्चर कर सकता है जो ऑब्जेक्ट के उदाहरण का प्रतिनिधित्व करता है जिसे बाहरी फ़ंक्शन कहा जाता था। इस जोड़कर किया जाता है this पर कब्जा सूची:

class Foo
{
private:
    int i;
    
public:
    Foo(int val) : i(val) {}
    
    void Test()
    {
        // capture the this pointer by value
        auto lamb = [this](int val)
        {
            i = val;
        };
        
        lamb(30);
    }
};

जब this पर कब्जा कर लिया जाता है, तो लैम्ब्डा अपने युक्त वर्ग के सदस्य नामों का उपयोग कर सकता है, जैसे कि वह अपनी युक्त कक्षा में था। तो this-> तरह के सदस्यों के लिए एक निहितार्थ लागू होता है।

विदित हो कि this मूल्य द्वारा कैप्चर किया गया है, लेकिन प्रकार का मूल्य नहीं। यह this के मूल्य द्वारा कब्जा कर लिया जाता है, जो एक सूचक है । जैसे, लैम्ब्डा का स्वामी नहीं है this । यदि लैम्ब्डा आजीवन उस वस्तु का निर्माण करता है जो इसे बनाया है, तो लैम्ब्डा अमान्य हो सकता है।

यह भी मतलब है लैम्ब्डा संशोधित कर सकते हैं कि this घोषित जा रहा है बिना mutable । यह पॉइंटर है जो const , न कि ऑब्जेक्ट को इंगित किया जा रहा है। यह है, जब तक कि बाहरी सदस्य फ़ंक्शन स्वयं एक const फ़ंक्शन नहीं था।

इसके अलावा, ध्यान रखें कि डिफ़ॉल्ट कैप्चर क्लॉस, दोनों [=] और [&] , this निहितार्थ को भी कैप्चर करेगा। और वे दोनों सूचक के मूल्य से इसे पकड़ते हैं। दरअसल, डिफॉल्ट दिए जाने पर कैप्चर सूची में this निर्दिष्ट करना एक त्रुटि है।

सी ++ 17

लैंबडा this वस्तु की एक प्रति कैप्चर कर सकता है, जिस समय लैंबडा बनाया जाता है। यह कैप्चर सूची में *this जोड़कर किया जाता है:

class Foo
{
private:
    int i;
    
public:
    Foo(int val) : i(val) {}
    
    void Test()
    {
        // capture a copy of the object given by the this pointer
        auto lamb = [*this](int val) mutable
        {
            i = val;
        };
        
        lamb(30); // does not change this->i
    }
};

पोर्टिंग लैम्बडा फंक्शंस का उपयोग करके C ++ 03 में कार्य करता है

सी में Lambda प्रकार्य ++ वाक्यात्मक चीनी कि लिखने के लिए एक बहुत ही संक्षिप्त वाक्य रचना प्रदान कर रहे हैं functors । इस प्रकार, लैम्बडा फ़ंक्शन को एक फंक्शनल में परिवर्तित करके C ++ 03 (यद्यपि अधिक वर्बोज़) में समान कार्यक्षमता प्राप्त की जा सकती है:

// Some dummy types:
struct T1 {int dummy;};
struct T2 {int dummy;};
struct R {int dummy;};

// Code using a lambda function (requires C++11)
R use_lambda(T1 val, T2 ref) {
  // Use auto because the type of the lambda is unknown.
  auto lambda = [val, &ref](int arg1, int arg2) -> R {
    /* lambda-body */
    return R();
  };
  return lambda(12, 27);
}

// The functor class (valid C++03)
// Similar to what the compiler generates for the lambda function.
class Functor {
  // Capture list.
  T1 val;
  T2& ref;

public:
  // Constructor
  inline Functor(T1 val, T2& ref) : val(val), ref(ref) {}

  // Functor body
  R operator()(int arg1, int arg2) const {
    /* lambda-body */
    return R();
  }
};

// Equivalent to use_lambda, but uses a functor (valid C++03).
R use_functor(T1 val, T2 ref) {
  Functor functor(val, ref);
  return functor(12, 27);
}

// Make this a self-contained example.
int main() {
  T1 t1;
  T2 t2;
  use_functor(t1,t2);
  use_lambda(t1,t2);
  return 0;
}

यदि लैंबडा फंक्शन mutable तो फ़नकार के कॉल-ऑपरेटर को नॉन-कास्ट करें, अर्थात:

R operator()(int arg1, int arg2) /*non-const*/ {
  /* lambda-body */
  return R();
}

पुनरावर्ती लंबोदर

मान लें कि हम यूक्लिड की gcd() को gcd() रूप में लिखना चाहते हैं। एक समारोह के रूप में, यह है:

int gcd(int a, int b) {
    return b == 0 ? a : gcd(b, a%b);
}

लेकिन एक लंबोदर पुनरावर्ती नहीं हो सकता, उसके पास खुद को आह्वान करने का कोई तरीका नहीं है। एक लैम्ब्डा कोई नाम नहीं है और का उपयोग कर this एक लैम्ब्डा के मुख्य भाग में एक पर कब्जा कर लिया है को संदर्भित करता this (लैम्ब्डा एक सदस्य समारोह के मुख्य भाग में बनाई गई है यह सोचते हैं, अन्यथा यह एक त्रुटि है)। तो हम इस समस्या को कैसे हल करते हैं?

std::function उपयोग करें

हम एक लंबोदर को एक अभी तक निर्मित std::function संदर्भ में कैप्चर कर सकते हैं std::function :

std::function<int(int, int)> gcd = [&](int a, int b){
    return b == 0 ? a : gcd(b, a%b);
};

यह काम करता है, लेकिन संयम से इस्तेमाल किया जाना चाहिए। यह धीमा है (हम एक प्रत्यक्ष फ़ंक्शन कॉल के बजाय अब टाइप एरेस्योर का उपयोग कर रहे हैं), यह नाजुक है ( gcd कॉपी करना या gcd को वापस लौटना होगा क्योंकि लैम्ब्डा मूल ऑब्जेक्ट को संदर्भित करता है), और यह जेनेरिक लैम्ब्डा के साथ काम नहीं करेगा।

दो स्मार्ट पॉइंटर्स का उपयोग करना:

auto gcd_self = std::make_shared<std::unique_ptr< std::function<int(int, int)> >>();
*gcd_self = std::make_unique<std::function<int(int, int)>>(
  [gcd_self](int a, int b){
    return b == 0 ? a : (**gcd_self)(b, a%b);
  };
};

यह बहुत कुछ अप्रत्यक्ष जोड़ता है (जो कि ओवरहेड है), लेकिन इसे कॉपी / लौटाया जा सकता है, और सभी प्रतियाँ राज्य साझा करती हैं। यह आपको लैम्ब्डा को वापस करने देता है, और अन्यथा उपरोक्त समाधान की तुलना में कम नाजुक है।

वाई-कॉम्बिनेटर का उपयोग करें

एक छोटी उपयोगिता संरचना की मदद से, हम इन सभी समस्याओं को हल कर सकते हैं:

template <class F>
struct y_combinator {
    F f; // the lambda will be stored here

    // a forwarding operator():
    template <class... Args>
    decltype(auto) operator()(Args&&... args) const {
        // we pass ourselves to f, then the arguments.
        // the lambda should take the first argument as `auto&& recurse` or similar.
        return f(*this, std::forward<Args>(args)...);
    }
};
// helper function that deduces the type of the lambda:
template <class F>
y_combinator<std::decay_t<F>> make_y_combinator(F&& f) {
    return {std::forward<F>(f)};
}
// (Be aware that in C++17 we can do better than a `make_` function)

हम अपने gcd को इस प्रकार लागू कर सकते हैं:

auto gcd = make_y_combinator(
  [](auto&& gcd, int a, int b){
    return b == 0 ? a : gcd(b, a%b);
  }
);

y_combinator लैम्ब्डा कैलकुलस की एक अवधारणा है जो आपको परिभाषित किए बिना अपने आप का नाम लिए बिना पुनरावृत्ति की सुविधा देता है। यह वास्तव में समस्या है लैम्ब्डा है।

आप एक लैम्ब्डा बनाते हैं जो "रिकर्स" को इसके पहले तर्क के रूप में लेता है। जब आप पुनरावृत्ति करना चाहते हैं, तो आप पुनरावृत्ति के लिए तर्क पास करते हैं।

y_combinator तब एक फ़ंक्शन ऑब्जेक्ट देता है जो उस फ़ंक्शन को उसके तर्कों के साथ कहता है, लेकिन एक उपयुक्त " y_combinator " ऑब्जेक्ट (अर्थात् y_combinator स्वयं) के साथ उसके पहले तर्क के रूप में। यह बाकी के तर्कों को आगे ले जाता है जिन्हें आप y_combinator को लैम्ब्डा के साथ भी कहते हैं।

संक्षेप में:

auto foo = make_y_combinator( [&](auto&& recurse, some arguments) {
  // write body that processes some arguments
  // when you want to recurse, call recurse(some other arguments)
});

और आपके पास एक लैम्ब्डा में पुनरावृत्ति है जिसमें कोई गंभीर प्रतिबंध या महत्वपूर्ण ओवरहेड नहीं है।

इनलाइन पैरामीटर पैक अनपैकिंग के लिए लैम्ब्डा का उपयोग करना

सी ++ 14

व्यास पैक पारंपरिक रूप से हर बार जब आप इसे करना चाहते हैं, तो एक सहायक फ़ंक्शन लिखने की आवश्यकता होती है।

इस खिलौना उदाहरण में:

template<std::size_t...Is>
void print_indexes( std::index_sequence<Is...> ) {
  using discard=int[];
  (void)discard{0,((void)(
    std::cout << Is << '\n' // here Is is a compile-time constant.
  ),0)...};
}
template<std::size_t I>
void print_indexes_upto() {
  return print_indexes( std::make_index_sequence<I>{} );
}

print_indexes_upto इंडेक्स का पैरामीटर पैक बनाना और अनपैक करना चाहता है। ऐसा करने के लिए, उसे एक सहायक फ़ंक्शन को कॉल करना होगा। हर बार जब आप अपने द्वारा बनाए गए पैरामीटर पैक को अनपैक करना चाहते हैं, तो आप इसे करने के लिए एक कस्टम हेल्पर फ़ंक्शन बनाते हैं।

इससे लंबोदर से बचा जा सकता है।

आप इस तरह से लैम्बडा के इनवोकेशन के एक सेट में पैरामीटर पैक्स को अनपैक कर सकते हैं:

template<std::size_t I>
using index_t = std::integral_constant<std::size_t, I>;
template<std::size_t I>
constexpr index_t<I> index{};

template<class=void, std::size_t...Is>
auto index_over( std::index_sequence<Is...> ) {
  return [](auto&& f){
    using discard=int[];
    (void)discard{0,(void(
      f( index<Is> )
    ),0)...};
  };
}

template<std::size_t N>
auto index_over(index_t<N> = {}) {
  return index_over( std::make_index_sequence<N>{} );
}
सी ++ 17

गुना अभिव्यक्तियों के साथ, index_over() को सरल बनाया जा सकता है:

template<class=void, std::size_t...Is>
auto index_over( std::index_sequence<Is...> ) {
  return [](auto&& f){
    ((void)(f(index<Is>)), ...);
  };
}

एक बार जब आप ऐसा कर लेते हैं, तो आप इसका उपयोग दूसरे कोड में दूसरे ओवरलोड के साथ मैन्युअल रूप से अनपैक पैरामीटर पैक को बदलने के लिए कर सकते हैं, जिससे आपको पैरामीटर पैक को "इनलाइन" करने की अनुमति मिलती है:

template<class Tup, class F>
void for_each_tuple_element(Tup&& tup, F&& f) {
  using T = std::remove_reference_t<Tup>;
  using std::tuple_size;
  auto from_zero_to_N = index_over< tuple_size<T>{} >();

  from_zero_to_N(
    [&](auto i){
      using std::get;
      f( get<i>( std::forward<Tup>(tup) ) );
    }
  );
}

auto i index_over द्वारा लैम्ब्डा के पास गया एक std::integral_constant<std::size_t, ???> । इसका std::size_t एक constexpr रूपांतरण है जो this की स्थिति पर निर्भर नहीं करता है, इसलिए हम इसे संकलन-समय स्थिरांक के रूप में उपयोग कर सकते हैं, जैसे कि जब हम इसे पास करते हैं तो std::get<i> ऊपर।

शीर्ष पर खिलौना के उदाहरण पर वापस जाने के लिए, इसे इस प्रकार लिखें:

template<std::size_t I>
void print_indexes_upto() {
  index_over(index<I>)([](auto i){
    std::cout << i << '\n'; // here i is a compile-time constant
  });
}

जो बहुत छोटा है, और इसका उपयोग करने वाले कोड में तर्क रखता है।

साथ निभाने के लिए जीवंत उदाहरण



Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow