ข้ามไปที่เนื้อหา

Ch14: Lambda functions

TL;DR: Anonymous Functionality: Lambda Function คือฟังก์ชันที่ไม่มีชื่อ (Anonymous Function) ซึ่งช่วยอำนวยความสะดวกในการนิยามฟังก์ชันแบบ inline ในตําแหน่งที่ต้องการใช้งานได้ทันที

⚡ Quick Reference

#include <iostream>

int main() {
    // [capture](params) -> ret { body }; - ไวยากรณ์เต็มรูปแบบของ Lambda
    auto full_lambda = [](double a, double b) -> double { return a + b; };

    // auto func = []{ ... }; - กำหนด Lambda ให้กับตัวแปรประเภท auto
    auto simple_lambda = []() { std::cout << "Simple" << std::endl; };
    simple_lambda();

    // []{ ... }(); - ประกาศสร้างและเรียกใช้งาน Lambda ทันที
    []() { std::cout << "One-shot" << std::endl; }();

    int a = 10, b = 20;
    // [a, b] - ดึงตัวแปรที่ระบุเข้ามาแบบสำเนาค่า (By value)
    auto val_lambda = [a, b]() { return a + b; };

    // [&a, &b] - ดึงตัวแปรที่ระบุเข้ามาแบบอ้างอิงตำแหน่งในแรม (By reference)
    auto ref_lambda = [&a, &b]() { a += 10; };

    // [=] - ดึงตัวแปรภายนอกทั้งหมดเข้ามาแบบสำเนาค่า (By value)
    auto capture_all_val = [=]() { return a + b; };

    // [&] - ดึงตัวแปรภายนอกทั้งหมดเข้ามาแบบอ้างอิงตำแหน่งในแรม (By reference)
    auto capture_all_ref = [&]() { a = 100; };

    // [] -> int { ... } - ระบุประเภทข้อมูลส่งกลับของ Lambda ชัดเจน
    auto forced_ret = []() -> int { return 42; };

    // เรียกใช้ Lambda พร้อมอาร์กิวเมนต์เพื่อทำงานทันที
    double sum = [](double x, double y){ return x + y; }(10.5, 20.5);

    return 0;
}

🧠 Core Concepts

  • Anonymous Functionality: Lambda Function คือฟังก์ชันที่ไม่มีชื่อ (Anonymous Function) ซึ่งช่วยอำนวยความสะดวกในการนิยามฟังก์ชันแบบ inline ในตําแหน่งที่ต้องการใช้งานได้ทันที
  • Variable Capture: Lambda จะเข้าถึงตัวแปรภายนอกได้ก็ต่อเมื่อระบุใน Capture List ([]) เท่านั้น โดยโดยเริ่มต้นจะไม่สามารถอ่านเขียนตัวแปรภายนอกสโคปได้เลย
  • Return Type Deduction: Compiler จะช่วยคาดเดาประเภทข้อมูลส่งกลับของ Lambda ให้โดยอัตโนมัติ แต่ผู้พัฒนาสามารถระบุประเภทเองได้ชัดเจนผ่านไวยากรณ์ลูกศรชี้ออก ->

⚠️ Pitfalls (Quick Scan)

ข้อผิดพลาด วิธีแก้
ประกาศสร้าง Lambda โดยไม่ได้เรียกคำสั่งให้ทำงานหรือเก็บค่าลงตัวแปร เรียกทำงานทันทีโดยใส่วงเล็บ () ต่อท้าย หรือเก็บค่าไว้ในตัวแปร auto
เรียกใช้งานตัวแปรภายนอกในบล็อก Lambda โดยไม่เขียนไว้ใน capture list เขียนระบุตัวแปรที่ต้องการนำเข้ามาใช้งานในวงเล็บเหลี่ยม []
คาดหวังให้ตัวแปรภายนอกที่เปลี่ยนไปส่งผลต่อข้อมูลใน Lambda ที่ดึงค่าแบบส่งผ่านค่า (Capture by value) ดึงตัวแปรแบบอ้างอิง ([&]) หากต้องการให้สะท้อนค่าที่เปลี่ยนแปลงล่าสุด
ดึงตัวแปรภายนอกแบบอ้างอิงทิ้งไว้ใน Lambda แต่ตัวแปรต้นฉบับหมดอายุขัยไปก่อนเรียกใช้ ตรวจสอบขอบเขตอายุขัย (Lifetime) ของตัวแปรภายนอกให้ครอบคลุมการทำงานของ Lambda
ระบุประเภทการส่งคืนแบบแคบลง (เช่น ` ตัวเลขจะถูกปัดเศษหรือตัดส่วนท้ายออกเงียบๆ ทำให้สูญเสียความแม่นยำ
ส่งผ่านค่าคนละชนิดเข้าไปในพารามิเตอร์ของ Lambda ป้อนอาร์กิวเมนต์ให้ตรงประเภทกับที่ระบุในรายการพารามิเตอร์

📖 Full Details

Cause → Effect → Fix พร้อม timestamp (คลิกเพื่อดู) * **ประกาศสร้าง Lambda โดยไม่ได้เรียกคำสั่งให้ทำงานหรือเก็บค่าลงตัวแปร** -> **โค้ดผ่านการ Compile ตามปกติแต่ไม่มีอะไรทำงานเลย** -> **เรียกทำงานทันทีโดยใส่วงเล็บ `()` ต่อท้าย หรือเก็บค่าไว้ในตัวแปร `auto` (00:04:17)** * **เรียกใช้งานตัวแปรภายนอกในบล็อก Lambda โดยไม่เขียนไว้ใน capture list** -> **เกิด Compile-time error แจ้งเตือนว่าห้ามอ้างอิงตัวแปรภายนอก** -> **เขียนระบุตัวแปรที่ต้องการนำเข้ามาใช้งานในวงเล็บเหลี่ยม `[]` (00:15:51)** * **คาดหวังให้ตัวแปรภายนอกที่เปลี่ยนไปส่งผลต่อข้อมูลใน Lambda ที่ดึงค่าแบบส่งผ่านค่า (Capture by value)** -> **ตัวแปรภายใน Lambda จะยึดตามค่าที่คัดลอกไว้ตั้งแต่จังหวะสร้างฟังก์ชันเท่านั้น** -> **ดึงตัวแปรแบบอ้างอิง (`[&]`) หากต้องการให้สะท้อนค่าที่เปลี่ยนแปลงล่าสุด (00:14:30)** * **ดึงตัวแปรภายนอกแบบอ้างอิงทิ้งไว้ใน Lambda แต่ตัวแปรต้นฉบับหมดอายุขัยไปก่อนเรียกใช้** -> **เกิด Undefined behavior หรือโปรแกรมแครชเนื่องจากอ่านค่าตำแหน่งขยะ** -> **ตรวจสอบขอบเขตอายุขัย (Lifetime) ของตัวแปรภายนอกให้ครอบคลุมการทำงานของ Lambda (00:18:30)** * **ระบุประเภทการส่งคืนแบบแคบลง (เช่น `-> int`) เกินผลลัพธ์การคำนวณจริง** -> **ตัวเลขจะถูกปัดเศษหรือตัดส่วนท้ายออกเงียบๆ ทำให้สูญเสียความแม่นยำ** -> **เลือกประเภทผลลัพธ์ให้ครอบคลุมขนาดข้อมูลเพื่อเลี่ยง narrowing (00:12:27)** * **ส่งผ่านค่าคนละชนิดเข้าไปในพารามิเตอร์ของ Lambda** -> **Compiler จะลอบทำการแปลงประเภทข้อมูลโดยนัย ซึ่งเสี่ยงทำให้ข้อมูลคลาดเคลื่อน** -> **ป้อนอาร์กิวเมนต์ให้ตรงประเภทกับที่ระบุในรายการพารามิเตอร์ (00:07:56)**

📎 Repo Files

  • 21.LambdaFunctions/21.2DeclaringAndUsingLambdas/main.cpp
  • 21.LambdaFunctions/21.3CaptureLists/main.cpp
  • 21.LambdaFunctions/21.4CaptureAllLists/main.cpp
  • 22.FunctionsTheMisfits/22.2StaticVariables/main.cpp
  • 22.FunctionsTheMisfits/22.3InlineFunctions/main.cpp
  • 22.FunctionsTheMisfits/22.4RecursiveFunctions/main.cpp
  • 23.FunctionCallStackD_ebugging/23.4D_ebuggingInV_S_C0de/main.cpp
  • 23.FunctionCallStackD_ebugging/23.7D_ebuggingArraysLoopsAndPointers/main.cpp