แลมบ์ดาในคำถามจริงมีรัฐไม่มี
ตรวจสอบ:
struct lambda {
auto operator()() const { return 17; }
};
และถ้าเรามีlambda f;
มันก็เป็นคลาสที่ว่างเปล่า ไม่เพียง แต่lambda
ฟังก์ชันข้างต้นจะคล้ายกับแลมด้าของคุณเท่านั้น แต่ยังเป็นวิธีการนำแลมด้าของคุณไปใช้งาน (โดยทั่วไป)! (นอกจากนี้ยังต้องการตัวดำเนินการชี้โดยนัยเพื่อใช้งานตัวชี้และชื่อlambda
จะถูกแทนที่ด้วย pseudo-guid ที่สร้างโดยคอมไพเลอร์)
ใน C ++ วัตถุไม่ใช่ตัวชี้ เป็นสิ่งที่เกิดขึ้นจริง พวกเขาใช้พื้นที่ที่จำเป็นในการจัดเก็บข้อมูลในนั้นเท่านั้น ตัวชี้ไปยังวัตถุอาจมีขนาดใหญ่กว่าวัตถุ
แม้ว่าคุณอาจคิดว่าแลมด้านั้นเป็นตัวชี้ไปยังฟังก์ชัน แต่ก็ไม่ใช่ คุณไม่สามารถกำหนดauto f = [](){ return 17; };
ให้เป็นฟังก์ชันอื่นหรือแลมด้าได้!
auto f = [](){ return 17; };
f = [](){ return -42; };
ดังกล่าวเป็นความผิดกฎหมาย มีห้องพักไม่มีคือf
การจัดเก็บซึ่งฟังก์ชั่นเป็นไปได้เรียกว่า - ว่าข้อมูลจะถูกเก็บไว้ในประเภทของการf
ไม่ได้อยู่ในค่าของf
!
หากคุณทำสิ่งนี้:
int(*f)() = [](){ return 17; };
หรือสิ่งนี้:
std::function<int()> f = [](){ return 17; };
คุณไม่ได้จัดเก็บแลมด้าโดยตรงอีกต่อไป ในทั้งสองกรณีนี้f = [](){ return -42; }
เป็นกฎหมาย - ดังนั้นในกรณีเหล่านี้เรามีการจัดเก็บที่f
ทำงานเราจะกล่าวอ้างในค่าของ และsizeof(f)
ไม่มีอีกต่อไป1
แต่จะมากกว่าsizeof(int(*)())
หรือใหญ่กว่า (โดยทั่วไปให้มีขนาดตัวชี้หรือใหญ่ขึ้นตามที่คุณคาดหวัง std::function
มีขนาดขั้นต่ำที่บ่งบอกโดยนัยตามมาตรฐาน (พวกเขาต้องสามารถจัดเก็บคำเรียกที่ "ภายในตัวเอง" ได้ถึงขนาดที่กำหนด) ซึ่ง อย่างน้อยก็มีขนาดใหญ่เท่ากับตัวชี้ฟังก์ชันในทางปฏิบัติ)
ในint(*f)()
กรณีนี้คุณกำลังจัดเก็บตัวชี้ฟังก์ชันไปยังฟังก์ชันที่ทำงานราวกับว่าคุณเรียกแลมด้านั้น ใช้ได้เฉพาะกับ lambdas ไร้สัญชาติเท่านั้น ( []
รายการที่มีรายการจับว่าง)
ในstd::function<int()> f
กรณีนี้คุณกำลังสร้างstd::function<int()>
อินสแตนซ์คลาส type-erasure ที่ (ในกรณีนี้) ใช้ตำแหน่งใหม่ในการจัดเก็บสำเนาของแลมบ์ดาขนาด -1 ในบัฟเฟอร์ภายใน (และหากมีการส่งแลมบ์ดาที่ใหญ่กว่า (ที่มีสถานะมากกว่า ) จะใช้การจัดสรรฮีป)
เดาว่าสิ่งเหล่านี้น่าจะเป็นสิ่งที่คุณคิดว่าเกิดขึ้น แลมด้าเป็นวัตถุที่มีการอธิบายประเภทด้วยลายเซ็น ใน C ++ มีการตัดสินใจที่จะสร้างabstractions ต้นทุนแลมบ์ดาสเป็นศูนย์สำหรับการใช้อ็อบเจ็กต์ฟังก์ชันแมนนวล วิธีนี้ช่วยให้คุณส่งแลมด้าไปยังstd
อัลกอริทึม (หรือที่คล้ายกัน) และให้คอมไพเลอร์มองเห็นเนื้อหาได้อย่างสมบูรณ์เมื่อสร้างอินสแตนซ์เทมเพลตอัลกอริทึม ถ้าแลมด้ามีประเภทเหมือนstd::function<void(int)>
เนื้อหาของมันจะไม่สามารถมองเห็นได้อย่างสมบูรณ์และอ็อบเจ็กต์ฟังก์ชันที่สร้างขึ้นด้วยมืออาจเร็วกว่า
เป้าหมายของการกำหนดมาตรฐาน C ++ คือการเขียนโปรแกรมระดับสูงโดยมีค่าใช้จ่ายเป็นศูนย์เหนือโค้ด C ที่สร้างขึ้นด้วยมือ
ตอนนี้คุณเข้าใจแล้วว่าคุณf
เป็นคนไร้สัญชาติจริงๆแล้วควรมีคำถามอื่นในหัวของคุณนั่นคือแลมด้าไม่มีสถานะ ทำไมขนาดไม่มี0
?
มีคำตอบสั้น ๆ
อ็อบเจ็กต์ทั้งหมดใน C ++ ต้องมีขนาดเล็กสุดเป็น 1 ภายใต้มาตรฐานและออบเจ็กต์ประเภทเดียวกันสองชิ้นไม่สามารถมีแอดเดรสเดียวกันได้ สิ่งเหล่านี้เชื่อมต่อกันเนื่องจากอาร์เรย์ของประเภทT
จะมีการวางองค์ประกอบsizeof(T)
แยก
ในขณะนี้เนื่องจากไม่มีสถานะบางครั้งจึงไม่สามารถใช้พื้นที่ได้ สิ่งนี้ไม่สามารถเกิดขึ้นได้เมื่อ "อยู่คนเดียว" แต่ในบางบริบทอาจเกิดขึ้นได้ std::tuple
และรหัสไลบรารีที่คล้ายกันใช้ประโยชน์จากข้อเท็จจริงนี้ นี่คือวิธีการทำงาน:
เนื่องจากแลมบ์ดาเทียบเท่ากับคลาสที่มีแลมด้าที่มีoperator()
ภาระงานมากเกินไปแลมบ์ดาไร้สัญชาติ (พร้อม[]
รายการจับภาพ) จึงเป็นคลาสว่างทั้งหมด พวกเขามีของsizeof
1
ในความเป็นจริงหากคุณได้รับมรดกจากพวกเขา (ซึ่งได้รับอนุญาต!) พวกเขาจะไม่ใช้พื้นที่ตราบใดที่ไม่ทำให้เกิดการชนกันของที่อยู่ประเภทเดียวกัน (ซึ่งเรียกว่าการเพิ่มประสิทธิภาพฐานว่าง)
template<class T>
struct toy:T {
toy(toy const&)=default;
toy(toy &&)=default;
toy(T const&t):T(t) {}
toy(T &&t):T(std::move(t)) {}
int state = 0;
};
template<class Lambda>
toy<Lambda> make_toy( Lambda const& l ) { return {l}; }
sizeof(make_toy( []{std::cout << "hello world!\n"; } ))
คือsizeof(int)
(ดีดังกล่าวข้างต้นเป็นสิ่งผิดกฎหมายเพราะคุณไม่สามารถสร้างแลมบ์ดาในบริบทที่ไม่ใช่การประเมิน: คุณต้องสร้างชื่อauto toy = make_toy(blah);
แล้วทำsizeof(blah)
แต่ที่เป็นเพียงเสียง) sizeof([]{std::cout << "hello world!\n"; })
ยังคงเป็น1
(คุณสมบัติที่คล้ายกัน)
ถ้าเราสร้างของเล่นประเภทอื่น:
template<class T>
struct toy2:T {
toy2(toy2 const&)=default;
toy2(T const&t):T(t), t2(t) {}
T t2;
};
template<class Lambda>
toy2<Lambda> make_toy2( Lambda const& l ) { return {l}; }
นี่มีแลมด้าสองสำเนา เนื่องจากไม่สามารถใช้ที่อยู่เดียวกันsizeof(toy2(some_lambda))
ได้2
!
struct
with anoperator()
)