แลมบ์ดาชนิดใดเมื่ออนุมานด้วย“ auto” ใน C ++ 11


142

ฉันเข้าใจว่าแลมบ์ดาเป็นตัวชี้ฟังก์ชั่น เมื่อฉันทำการทดสอบต่อไปนี้ฉันพบว่ามันผิด ( สาธิต )

#define LAMBDA [] (int i) -> long { return 0; }
int main ()
{
  long (*pFptr)(int) = LAMBDA;  // ok
  auto pAuto = LAMBDA;  // ok
  assert(typeid(pFptr) == typeid(pAuto));  // assertion fails !
}

รหัสข้างต้นหายไปจุดใด? ถ้าไม่เช่นtypeofนั้นแลมบ์ดานิพจน์คืออะไรเมื่ออนุมานด้วยautoคำหลัก?


8
“ ประเภทของแลมบ์ดาเป็นตัวชี้ฟังก์ชั่น” - นั่นจะไม่มีประสิทธิภาพและพลาดจุดรวมของแลมบ์ดา
Konrad Rudolph

คำตอบ:


145

ประเภทของการแสดงออกแลมบ์ดาไม่ได้ระบุไว้

แต่โดยทั่วไปพวกมันจะเป็นเพียงน้ำตาล syntactic สำหรับหน้าที่ แลมบ์ดาแปลโดยตรงไปเป็นนักแสดง สิ่งใดที่อยู่ภายใน[]จะกลายเป็นตัวสร้างพารามิเตอร์และสมาชิกของวัตถุ functor และพารามิเตอร์ที่อยู่ภายใน()จะกลายเป็นพารามิเตอร์สำหรับ operator()functor

แลมบ์ดาที่จับตัวแปรไม่ได้ (ไม่มีอะไรอยู่ข้างใน[]) สามารถแปลงเป็นตัวชี้ฟังก์ชั่น (MSVC2010 ไม่สนับสนุนสิ่งนี้หากเป็นคอมไพเลอร์ของคุณ แต่การแปลงนี้เป็นส่วนหนึ่งของมาตรฐาน)

แต่แลมบ์ดาชนิดที่แท้จริงไม่ใช่ตัวชี้ฟังก์ชัน เป็นประเภท functor ที่ไม่ระบุ


1
MSVC2010 ไม่รองรับการแปลงเป็นตัวชี้ฟังก์ชั่น แต่ MSVC11 ทำ blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx
KindDragon

18
+1 สำหรับ "น้ำตาล syntactic เพียงหน้าที่" ความสับสนที่อาจเกิดขึ้นสามารถหลีกเลี่ยงได้โดยการจดจำสิ่งนี้
Ben

4
functor เป็นอะไรกับoperator()พื้นstackoverflow.com/questions/356950/c-functors-and-their-uses
TankorSmash

107

มันเป็นโครงสร้างที่ไม่มีชื่อที่ไม่ซ้ำใครซึ่งทำให้ผู้ประกอบการเรียกฟังก์ชั่นโอเวอร์โหลด ตัวอย่างของแลมบ์ดาแนะนำประเภทใหม่

ในกรณีพิเศษของแลมบ์ดาที่ไม่มีการจับภาพโครงสร้างเพิ่มเติมมีการแปลงโดยนัยไปยังตัวชี้ฟังก์ชัน


2
คำตอบที่ดี แม่นยำยิ่งกว่าของฉันมาก +1 :)
jalf

4
+1 สำหรับส่วนที่เป็นอันหนึ่งอันเดียวกันมันน่าประหลาดใจมากในตอนแรกและได้รับความสนใจ
Matthieu M.

ไม่ใช่ว่ามันสำคัญจริงๆ แต่เป็นชื่อที่ไม่มีชื่อจริงๆหรือเป็นเพียงชื่อที่ไม่ได้รวบรวมจนกว่าจะถึงเวลารวบรวม IOW ใช้ RTTI เพื่อค้นหาชื่อที่คอมไพเลอร์เลือกได้หรือไม่?
Ben

3
@Ben มันไม่มีชื่อและเท่าที่ภาษา C ++ เกี่ยวข้องไม่มีสิ่งเช่น "ชื่อคอมไพเลอร์ตัดสินใจ" ผลที่ได้type_info::name()คือการใช้งานที่กำหนดดังนั้นมันอาจกลับมาอะไร ในทางปฏิบัติคอมไพเลอร์จะตั้งชื่อประเภทเพื่อประโยชน์ของลิงเกอร์
avakar

1
เมื่อเร็ว ๆ นี้เมื่อถามคำถามนี้ฉันมักจะบอกว่าประเภทแลมบ์ดานั้นมีชื่อคอมไพเลอร์ก็รู้ดี
Andre Kostur

24

[C++11: 5.1.2/3]: ประเภทของแลมบ์ดา - นิพจน์ (ซึ่งเป็นประเภทของวัตถุปิด) เป็นประเภทที่ไม่ซ้ำกันและไม่มีชื่อประเภท - เรียกว่าประเภทปิด - ซึ่งมีคุณสมบัติอธิบายไว้ด้านล่าง ประเภทของคลาสนี้ไม่ใช่การรวม (8.5.1) ประเภทการปิดประกาศในขอบเขตที่เล็กที่สุดบล็อกขอบเขตชั้นเรียนหรือขอบเขต namespace ที่มีที่สอดคล้องกันแลมบ์ดาแสดงออก [ .. ]

ส่วนคำสั่งไปที่รายการคุณสมบัติที่แตกต่างของประเภทนี้ นี่คือไฮไลท์บางส่วน:

[C++11: 5.1.2/5]:ประเภทปิดสำหรับแลมบ์ดาแสดงออกมีประชาชนinlineผู้ประกอบการฟังก์ชั่นการโทร (13.5.4) ที่มีค่าพารามิเตอร์และชนิดกลับมีการอธิบายโดยแลมบ์ดาแสดงออกของพารามิเตอร์การประกาศข้อและต่อท้ายผลตอบแทนชนิดตามลำดับ [ .. ]

[C++11: 5.1.2/6]:ประเภทการปิดสำหรับlambda-expression ที่ไม่มีlambda-captureมีฟังก์ชั่นการแปลง const แบบ non-virtual non-virtual ที่ไม่ชัดเจนเพื่อชี้ไปยังฟังก์ชั่นที่มีพารามิเตอร์เดียวกันและประเภทการส่งคืนเป็นตัวดำเนินการเรียกฟังก์ชันของ ค่าที่ส่งคืนโดยฟังก์ชันการแปลงนี้จะเป็นที่อยู่ของฟังก์ชันที่เมื่อเรียกใช้จะมีผลเช่นเดียวกับการเรียกใช้ตัวดำเนินการเรียกฟังก์ชันของประเภทการปิด

ผลที่ตามมาของทางเดินสุดท้ายนี้ก็คือว่าถ้าคุณใช้การแปลงที่คุณจะสามารถที่จะกำหนดไปLAMBDApFptr


3
#include <iostream>
#include <typeinfo>

#define LAMBDA [] (int i)->long { return 0l; }
int main ()
{
  long (*pFptr)(int) = LAMBDA;  // ok
  auto pAuto = LAMBDA;  // ok

  std::cout<<typeid( *pAuto ).name() << std::endl;
  std::cout<<typeid( *pFptr ).name() << std::endl;

  std::cout<<typeid( pAuto ).name() << std::endl;
  std::cout<<typeid( pFptr ).name() << std::endl;
}

ประเภทฟังก์ชั่นนั้นเหมือนกัน แต่แลมบ์ดาแนะนำประเภทใหม่ (เช่น functor)


ฉันแนะนำCXXABI แบบ unmangling วิธีถ้าคุณไปเส้นทางนี้แล้ว แต่ฉันมักจะใช้ประโยชน์จากสิ่ง__PRETTY_FUNCTION__ที่อยู่ข้างในtemplate<class T> const char* pretty(T && t) { return __PRETTY_FUNCTION__; }และดึงส่วนเกินออกหากมันเริ่มแออัด ฉันชอบที่จะเห็นขั้นตอนที่แสดงในการทดแทนแม่แบบ หากคุณขาดหายไป__PRETTY_FUNCTION__มีทางเลือกสำหรับ MSVC ฯลฯ แต่ผลลัพธ์นั้นขึ้นอยู่กับผู้รวบรวมด้วยเหตุผลเดียวกัน CXXABI เป็นสิ่งจำเป็น
จอห์น P

1

ควรสังเกตว่าแลมบ์ดานั้นสามารถแปลงเป็นตัวชี้ฟังก์ชันได้ อย่างไรก็ตาม typeid <> ส่งคืนออบเจกต์ที่ไม่ใช่ trvial ซึ่งควรแตกต่างจากแลมบ์ดาถึงตัวชี้ฟังก์ชันทั่วไป ดังนั้นการทดสอบ typeid <> จึงไม่ใช่ข้อสันนิษฐานที่ถูกต้อง โดยทั่วไป C ++ 11 ไม่ต้องการให้เรากังวลเกี่ยวกับข้อมูลจำเพาะของประเภทสิ่งที่สำคัญถ้าประเภทที่กำหนดนั้นสามารถแปลงเป็นประเภทเป้าหมายได้


เป็นเรื่องที่ยุติธรรม แต่ประเภทการพิมพ์มีความยาวมากพอที่จะไปถึงประเภทที่ถูกต้องไม่ต้องพูดถึงการจับเคสที่เป็นประเภทที่เปลี่ยนแปลงได้ แต่ไม่เป็นไปตามข้อ จำกัด อื่น ๆ (ฉันมักจะผลักดันไปสู่ข้อ จำกัด "reifying" ทุกที่ที่เป็นไปได้ แต่มีคนพยายามทำเช่นนั้นมีเหตุผลมากขึ้นเพื่อแสดงงานของพวกเขาในระหว่างการพัฒนา.)
John P

0

วิธีแก้ปัญหาในทางปฏิบัติจากฉันจะเก็บ boost :: bind object เป็นสมาชิกคลาสได้อย่างไร ลองหรือboost::function<void(int)>std::function<void(int)>


1
อย่างไรก็ตามควรตระหนักถึงต้นทุนประสิทธิภาพของสิ่งนี้ (การเรียกใช้ฟังก์ชันเสมือนจะเกิดขึ้นทุกครั้งที่มีการเรียกใช้ฟังก์ชัน)
HighCommander4
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.