ค่าปิดของแลมบ์ดาสามารถส่งผ่านเป็นพารามิเตอร์อ้างอิง rvalue ได้


18

ฉันพบว่าการlvalueปิดแลมบ์ดาสามารถส่งผ่านเป็นrvalueพารามิเตอร์ฟังก์ชันได้เสมอ

ดูการสาธิตง่ายๆดังต่อไปนี้

#include <iostream>
#include <functional>

using namespace std;

void foo(std::function<void()>&& t)
{
}

int main()
{
    // Case 1: passing a `lvalue` closure
    auto fn1 = []{};
    foo(fn1);                          // works

    // Case 2: passing a `lvalue` function object
    std::function<void()> fn2 = []{};
    foo(fn2);                          // compile error

    return 0;
}

กรณีที่ 2 เป็นพฤติกรรมมาตรฐาน (ฉันเพิ่งใช้std::functionเพื่อวัตถุประสงค์ในการสาธิต แต่ประเภทอื่น ๆ จะทำงานเหมือนกัน)

เคส 1 ทำงานอย่างไรและทำไม? สถานะการfn1ปิดหลังจากฟังก์ชั่นกลับมาคืออะไร?


5
ฉันเดาว่าเป็นเพราะfn1ถูกแปลงโดยปริยายไปในstd::function foo(fn1)ฟังก์ชั่นชั่วคราวนั้นเป็นค่า rvalue
eike

@RichardCritten ฉันไม่แน่ใจดังนั้นฉันจึงไม่ได้โพสต์คำตอบ ฉันคิดว่าตอนนี้ไม่จำเป็นต้องมีอีกคนหนึ่ง
eike

1
@eike np ฉันมักจะรู้สึกแบบเดียวกันและ yup คำตอบมากมาย
Richard Critten

2
@Sumudu คนที่ถามคำถามนั้นเข้าใจผิดคุณเพราะพวกเขาไม่รู้ว่าพวกเขากำลังพยายามจะถามอะไร สิ่งที่พวกเขาหมายถึงการถามคือ: "ทำไมไม่สามารถโต้แย้งแม่แบบของการstd::functionอนุมานจากแลมบ์ดา" โปรแกรมของคุณไม่พยายามสรุปอาร์กิวเมนต์ของเทมเพลตstd::functionดังนั้นจึงไม่มีปัญหากับการแปลงโดยนัย
eerorika

1
ชื่อคำถามที่คุณเชื่อมโยงนั้นทำให้เข้าใจผิดเล็กน้อย std::functionมีคอนสตรัคต์ที่ไม่ชัดเจนซึ่งยอมรับการปิดแลมบ์ดาดังนั้นจึงมีการแปลงโดยนัย แต่ในสถานการณ์ของคำถามที่เชื่อมโยงการสร้างอินสแตนซ์ของเทมเพลตstd::functionไม่สามารถอนุมานได้จากประเภทแลมบ์ดา (ตัวอย่างstd::function<void()>สามารถสร้างได้จาก[](){return 5;}แม้ว่ามันจะมีประเภทผลตอบแทนที่ไม่เป็นโมฆะ
eike

คำตอบ:


8

เคส 1 ทำงานอย่างไรและทำไม?

กล่าวอ้างfooต้องใช้อินสแตนซ์ของstd::function<void()>ผูกไปยังที่อ้างอิง rvalue std::function<void()>สามารถสร้างจากวัตถุที่สามารถโทรได้ใด ๆที่เข้ากันได้กับvoid()ลายเซ็น

ประการแรกชั่วคราววัตถุถูกสร้างขึ้นมาจากstd::function<void()> []{}ตัวสร้างที่ใช้คือ # 5 ที่นี่ซึ่งคัดลอกการปิดลงในstd::functionอินสแตนซ์:

template< class F >
function( F f );

std::move(f)เริ่มต้นเป้าหมายด้วย ถ้าfเป็นตัวชี้โมฆะในการทำงานหรือตัวชี้โมฆะให้กับสมาชิก*thisจะว่างเปล่าหลังจากการโทร

จากนั้นfunctionอินสแตนซ์ชั่วคราวจะถูกผูกไว้กับการอ้างอิง rvalue


สถานะของการปิด fn1 คืออะไรหลังจากฟังก์ชั่นกลับมา?

เหมือนเมื่อก่อนเพราะมันถูกคัดลอกไปยังstd::functionอินสแตนซ์ การปิดต้นฉบับไม่ได้รับผลกระทบ


8

std::functionแลมบ์ดาไม่ได้เป็น การอ้างอิงที่ไม่ได้ผูกโดยตรง

กรณีที่ 1 ใช้งานได้เพราะ lambdas สามารถแปลงเป็นstd::functions ได้ ซึ่งหมายความว่าชั่วคราวstd::functionมีรูปธรรมโดยการคัดลอก fn1กล่าวว่าชั่วคราวสามารถที่จะถูกผูกไว้กับการอ้างอิง rvalue และเพื่อให้อาร์กิวเมนต์ตรงกับพารามิเตอร์

และคัดลอกเป็นเหตุผลที่ได้รับผลกระทบทั้งหมดโดยสิ่งที่เกิดขึ้นในfn1foo


5

สถานะของการปิด fn1 คืออะไรหลังจากฟังก์ชั่นกลับมา?

fn1 ไร้สัญชาติเพราะมันจับอะไร

เคส 1 ทำงานอย่างไรและทำไม?

มันทำงานได้เนื่องจากข้อโต้แย้งเป็นประเภทที่แตกต่างจากประเภทที่อ้างอิง rvalue เนื่องจากมีประเภทที่แตกต่างกันจึงมีการพิจารณาการแปลงโดยนัย ตั้งแต่แลมบ์ดาเป็น Callable สำหรับอาร์กิวเมนต์ของเรื่องนี้มันเป็นโดยปริยายแปลงสภาพให้แก่มันผ่านแม่แบบการแปลงสร้างของstd::function std::functionผลลัพธ์ของการแปลงคือค่า prvalue และสามารถถูกรวมกับการอ้างอิง rvalue

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