แลมด้าทั่วไปทำงานอย่างไรใน C ++ 14


114

แลมบ์ดาทั่วไปทำงานอย่างไร ( autoคีย์เวิร์ดเป็นประเภทอาร์กิวเมนต์) ในมาตรฐาน C ++ 14

มันขึ้นอยู่กับแม่แบบ C ++ ที่คอมไพเลอร์ประเภทอาร์กิวเมนต์แต่ละตัวสร้างฟังก์ชันใหม่ที่มีเนื้อความเดียวกัน แต่แทนที่ประเภท (ความหลากหลายของเวลาคอมไพล์) หรือคล้ายกับชื่อสามัญของ Java (การลบประเภท) มากกว่าหรือไม่?

ตัวอย่างโค้ด:

auto glambda = [](auto a) { return a; };

6
แก้ไขเป็น C ++ 14 ซึ่งเดิมใช้ C ++ 11 ในคำถาม
sasha.sochka

คำตอบ:


130

lambdas ทั่วไปถูกนำมาใช้ในC++14.

กล่าวง่ายๆคือประเภทการปิดที่กำหนดโดยนิพจน์แลมบ์ดาจะมีตัวดำเนินการเรียกที่เป็นเทมเพลตแทนที่จะเป็นตัวดำเนินการเรียกแบบปกติที่ไม่ใช่เทมเพลตของC++11lambdas (แน่นอนเมื่อautoปรากฏอย่างน้อยหนึ่งครั้งในรายการพารามิเตอร์)

ตัวอย่างของคุณ:

auto glambda = [] (auto a) { return a; };

จะสร้างglambdaอินสแตนซ์ประเภทนี้:

class /* unnamed */
{
public:
    template<typename T>
    T operator () (T a) const { return a; }
};

ย่อหน้าที่ 5.1.2 / 5 ของ C ++ 14 Standard Draft n3690 ระบุวิธีการกำหนดตัวดำเนินการเรียกของประเภทการปิดของนิพจน์แลมบ์ดาที่กำหนด:

ประเภทการปิดสำหรับ lambda-expression ที่ไม่ใช่แบบทั่วไปมีตัวดำเนินการเรียกฟังก์ชันแบบอินไลน์สาธารณะ (13.5.4) ซึ่งพารามิเตอร์และประเภทการส่งคืนจะอธิบายโดยพารามิเตอร์การประกาศพารามิเตอร์ของแลมบ์ดานิพจน์และชนิดต่อท้าย - ส่งคืนตามลำดับ สำหรับแลมบ์ดาทั่วไปประเภทการปิดมีเทมเพลตสมาชิกตัวดำเนินการเรียกใช้ฟังก์ชันอินไลน์สาธารณะ (14.5.2) ซึ่งมีเทมเพลตพารามิเตอร์รายการประกอบด้วยพารามิเตอร์เทมเพลตชนิดที่คิดค้นขึ้นหนึ่งรายการสำหรับการเกิดอัตโนมัติแต่ละครั้งในพารามิเตอร์การประกาศพารามิเตอร์ของแลมบ์ดา ตามลำดับที่ปรากฏ. เทมเพลตพารามิเตอร์ชนิดที่คิดค้นขึ้นคือชุดพารามิเตอร์หากการประกาศพารามิเตอร์ที่เกี่ยวข้องประกาศชุดพารามิเตอร์ฟังก์ชัน (8.3.5) ประเภทการส่งคืนและพารามิเตอร์ฟังก์ชันของเทมเพลตตัวดำเนินการเรียกใช้ฟังก์ชันได้มาจากคำต่อท้ายชนิดผลตอบแทนและการประกาศพารามิเตอร์ของแลมบ์ดานิพจน์โดยการแทนที่การเกิดอัตโนมัติแต่ละครั้งในตัวระบุการปฏิเสธของพารามิเตอร์การประกาศ - อนุประโยคด้วยชื่อของ พารามิเตอร์เทมเพลตที่คิดค้นขึ้นที่สอดคล้องกัน

สุดท้าย:

คล้ายกับเทมเพลตหรือไม่ที่คอมไพเลอร์ประเภทอาร์กิวเมนต์แต่ละตัวสร้างฟังก์ชันที่มีเนื้อความเดียวกัน แต่เปลี่ยนประเภทหรือคล้ายกับ generics ของ Java มากกว่าหรือไม่?

ดังที่อธิบายไว้ในย่อหน้าข้างบน lambdas ทั่วไปเป็นเพียงน้ำตาลที่ใช้ในการสังเคราะห์สำหรับ functors ที่ไม่เหมือนใครและไม่มีชื่อด้วยตัวดำเนินการโทรแบบเทมเพล ที่ควรตอบคำถามของคุณ :)


7
อย่างไรก็ตามพวกเขายังอนุญาตคลาสที่กำหนดไว้ในเครื่องด้วยวิธีเทมเพลต ซึ่งเป็นเรื่องใหม่
Yakk - Adam Nevraumont

2
@Yakk: ข้อ จำกัด สำหรับเทมเพลตฟังก์ชันโลคัลไม่ได้ลดลงพร้อมกับ C ++ 11 แล้วหรือ
Sebastian Mach

2
@phresnel: ไม่ข้อ จำกัด นั้นยังไม่ถูกยกเลิก
Andy Prowl

1
@AndyProwl: ฉันตระหนักถึงความผิดพลาดของฉัน สิ่งที่ยกขึ้นคือการใช้ประเภทท้องถิ่นเป็นอาร์กิวเมนต์แม่แบบ (เช่นเดียวกับในint main () { struct X {}; std::vector<X> x; })
Sebastian Mach

1
@phresnel: ใช่แล้วที่เปลี่ยนไปแน่นอน
Andy Prowl

25

น่าเสียดายที่พวกเขาไม่ได้เป็นส่วนหนึ่งของ C ++ 11 ( http://ideone.com/NsqYuq ):

auto glambda = [](auto a) { return a; };

int main() {}

ด้วย g ++ 4.7:

prog.cpp:1:24: error: parameter declared auto
...

อย่างไรก็ตามวิธีที่อาจนำไปใช้ใน C ++ 14 ตามข้อเสนอของพอร์ตแลนด์สำหรับ lambdas ทั่วไป :

[](const& x, & y){ return x + y; }

สิ่งนี้จะส่งผลให้ส่วนที่ใหญ่ที่สุดคือการสร้างคลาส functor ที่ไม่ระบุชื่อตามปกติ แต่การขาดประเภทคอมไพเลอร์จะปล่อยสมาชิกที่เป็นเทมเพลต - operator():

struct anonymous
{
    template <typename T, typename U>
    auto operator()(T const& x, U& y) const -> decltype(x+y)
    { return x + y; }
};

หรือตามข้อเสนอ Proposal สำหรับนิพจน์ทั่วไป (Polymorphic) Lambda Expressions

auto L = [](const auto& x, auto& y){ return x + y; };

--->

struct /* anonymous */
{
    template <typename T, typename U>
    auto operator()(const T& x, U& y) const // N3386 Return type deduction
    { return x + y; }
} L;

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


6
ข้อเสนอที่อนุญาตให้ทิ้งตัวระบุประเภทนั้นเป็นเรื่องที่แปลกประหลาดอย่างยิ่ง
Lightness Races ใน Orbit

พวกเขาเดินไปด้วยกรัม ++ - 4.9 -std=c++1yคุณจำเป็นต้องจัดหา
emsr

ฉันไม่ทราบว่า ideone ยังไม่มี gcc-4.9 และ C ++ 14
emsr

คำถาม: สิ่งนี้autoมีกฎการหักเงินเหมือนกับรถยนต์คลาสสิกหรือไม่ หากเราอ้างถึงการเปรียบเทียบแบบเทมเพลจะหมายความว่าอัตโนมัติไม่ใช่อัตโนมัติซึ่งเป็นกฎเดียวกับการหักประเภทเทมเพลต คำถามคือการหักเทมเพลตเทียบเท่ากับauto?
v.oddou

@ v.oddou: "Classic auto" เป็นสิ่งที่ดี สำหรับฉันแล้ว "classic auto" หมายถึง "Stack Variable" และครั้งหนึ่งเคยใช้ในทางตรงกันข้ามกับstaticหรือregister:) อย่างไรก็ตามใช่แล้วการใช้ที่autoนั่นหมายความว่าภายใต้ประทุนจะมีการสร้างเทมเพลตปกติขึ้น ในความเป็นจริงแลมบ์ดาจะถูกแทนที่ด้วยคอมไพเลอร์ภายในด้วยคลาส functor และautoพารามิเตอร์หมายความว่าtemplate <T> ... (T ...)จะถูกปล่อยออกมา
Sebastian Mach

17

เป็นคุณลักษณะ C ++ 14 ที่เสนอ (ไม่ใช่ใน C ++ 11) ที่คล้ายกัน (หรือเทียบเท่า) กับเทมเพลต ตัวอย่างเช่นN3559ให้ตัวอย่างนี้:

ตัวอย่างเช่นแลมบ์ดานิพจน์ทั่วไปที่มีคำสั่ง:

auto L = [](const auto& x, auto& y){ return x + y; };

อาจส่งผลให้มีการสร้างประเภทการปิดและวัตถุที่ทำงานคล้ายกับโครงสร้างด้านล่าง:

struct /* anonymous */
{
    template <typename T, typename U>
    auto operator()(const T& x, U& y) const // N3386 Return type deduction
    { return x + y; }
} L;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.