ฉันจะส่ง std :: unique_ptr ไปยังฟังก์ชันได้อย่างไร


97

ฉันจะส่งผ่านstd::unique_ptrเข้าสู่ฟังก์ชันได้อย่างไร? บอกว่าฉันมีคลาสต่อไปนี้:

class A
{
public:
    A(int val)
    {
        _val = val;
    }

    int GetVal() { return _val; }
private:
    int _val;
};

ต่อไปนี้ไม่ได้รวบรวม:

void MyFunc(unique_ptr<A> arg)
{
    cout << arg->GetVal() << endl;
}

int main(int argc, char* argv[])
{
    unique_ptr<A> ptr = unique_ptr<A>(new A(1234));
    MyFunc(ptr);

    return 0;
}

เหตุใดฉันจึงไม่สามารถส่งผ่านstd::unique_ptrเข้าสู่ฟังก์ชันได้ แน่นอนว่านี่คือจุดประสงค์หลักของการสร้าง? หรือว่าคณะกรรมการ C ++ ตั้งใจให้ฉันถอยกลับไปใช้พอยน์เตอร์สไตล์ C แบบดิบแล้วส่งต่อแบบนี้:

MyFunc(&(*ptr)); 

และที่แปลกที่สุดทำไมนี่ถึงเป็นวิธีที่ใช้ได้? ดูเหมือนไม่สอดคล้องกันอย่างน่ากลัว:

MyFunc(unique_ptr<A>(new A(1234)));

7
ไม่มีอะไรผิดที่จะ "ถอยกลับ" ไปยังพอยน์เตอร์สไตล์ C แบบดิบตราบใดที่พวกเขาไม่ใช่ตัวชี้ดิบที่ไม่ได้เป็นเจ้าของ คุณอาจต้องการเขียน 'ptr.get ()' แม้ว่าหากคุณไม่ต้องการความว่างเปล่าควรใช้การอ้างอิง
Chris Drew

คำตอบ:


142

โดยทั่วไปมีสองตัวเลือกที่นี่:

ส่งสมาร์ทพอยน์เตอร์โดยอ้างอิง

void MyFunc(unique_ptr<A> & arg)
{
    cout << arg->GetVal() << endl;
}

int main(int argc, char* argv[])
{
    unique_ptr<A> ptr = unique_ptr<A>(new A(1234));
    MyFunc(ptr);
}

ย้ายตัวชี้อัจฉริยะไปที่อาร์กิวเมนต์ของฟังก์ชัน

โปรดทราบว่าในกรณีนี้การยืนยันจะถือ!

void MyFunc(unique_ptr<A> arg)
{
    cout << arg->GetVal() << endl;
}

int main(int argc, char* argv[])
{
    unique_ptr<A> ptr = unique_ptr<A>(new A(1234));
    MyFunc(move(ptr));
    assert(ptr == nullptr)
}

@VermillionAzure: คุณลักษณะที่คุณอ้างถึงโดยปกติเรียกว่า noncopyable ไม่ใช่เฉพาะ
Bill Lynch

1
ขอบคุณ. ในกรณีนี้ฉันต้องการสร้างอินสแตนซ์ให้ดำเนินการบางอย่างกับอินสแตนซ์จากนั้นจึงส่งต่อความเป็นเจ้าของให้กับบุคคลอื่นซึ่งการย้าย () ดูเหมือนจะสมบูรณ์แบบสำหรับ
user3690202

7
คุณควรส่งต่อunique_ptrโดยอ้างอิงเฉพาะในกรณีที่ฟังก์ชันอาจหรือไม่ย้ายไปจากฟังก์ชันนั้น จากนั้นควรเป็นข้อมูลอ้างอิง rvalue จะสังเกตเห็นวัตถุโดยไม่ต้องมีอะไรเกี่ยวกับความหมายของการเป็นเจ้าของของมันใช้การอ้างอิงเช่นหรือA const& A&
Potatoswatter

12
ฟัง Herb Sutters พูดคุยเกี่ยวกับ CppCon 2014 เขาไม่สนับสนุนอย่างยิ่งที่จะส่งต่อการอ้างอิงถึง unique_ptr <> สาระสำคัญคือคุณควรใช้ตัวชี้อัจฉริยะเช่น unique_ptr <> หรือ shared_ptr <> เมื่อคุณจัดการกับความเป็นเจ้าของ ดู www.youtube.com/watch?v=xnqTKD8uD64 ที่นี่คุณจะพบสไลด์ github.com/CppCon/CppCon2014/tree/master/Presentations/…
schorsch_76

2
@Furihr: มันเป็นเพียงวิธีแสดงให้เห็นว่าคุณค่าของptrมันคืออะไรหลังจากการเคลื่อนไหว
Bill Lynch

26

คุณส่งผ่านค่าซึ่งหมายถึงการทำสำเนา มันจะไม่ซ้ำกันมากใช่มั้ย?

คุณสามารถย้ายค่าได้ แต่นั่นหมายถึงการส่งผ่านความเป็นเจ้าของวัตถุและการควบคุมอายุการใช้งานไปยังฟังก์ชัน

หากอายุการใช้งานของวัตถุที่มีการประกันเพื่ออยู่ตลอดระยะเวลาของการเรียกร้องให้ MyFunc ptr.get()เพียงผ่านตัวชี้ดิบผ่าน


17

เหตุใดฉันจึงไม่สามารถส่งผ่านunique_ptrเข้าสู่ฟังก์ชันได้

คุณไม่สามารถทำได้เนื่องจากunique_ptrมีตัวสร้างการย้าย แต่ไม่ใช่ตัวสร้างการคัดลอก ตามมาตรฐานเมื่อกำหนดตัวสร้างการย้าย แต่ไม่ได้กำหนดตัวสร้างการคัดลอกตัวสร้างการคัดลอกจะถูกลบ

12.8 คัดลอกและย้ายคลาสออบเจ็กต์

...

7 ถ้านิยามคลาสไม่ได้ประกาศตัวสร้างการคัดลอกอย่างชัดเจนจะมีการประกาศตัวสร้างการคัดลอกโดยปริยาย ถ้านิยามคลาสประกาศตัวสร้างการย้ายหรือตัวดำเนินการกำหนดย้ายตัวสร้างการคัดลอกที่ประกาศโดยปริยายจะถูกกำหนดเป็นลบ

คุณสามารถส่งผ่านunique_ptrไปยังฟังก์ชันได้โดยใช้:

void MyFunc(std::unique_ptr<A>& arg)
{
    cout << arg->GetVal() << endl;
}

และใช้มันอย่างที่คุณมี:

หรือ

void MyFunc(std::unique_ptr<A> arg)
{
    cout << arg->GetVal() << endl;
}

และใช้มันเช่น:

std::unique_ptr<A> ptr = std::unique_ptr<A>(new A(1234));
MyFunc(std::move(ptr));

โน๊ตสำคัญ

โปรดทราบว่าหากคุณใช้วิธีที่สองptrจะไม่มีความเป็นเจ้าของตัวชี้หลังจากเรียกให้std::move(ptr)ส่งคืน

void MyFunc(std::unique_ptr<A>&& arg)จะมีผลเช่นเดียวกันvoid MyFunc(std::unique_ptr<A>& arg)เนื่องจากทั้งสองเป็นการอ้างอิง

ในกรณีแรกที่ยังคงมีความเป็นเจ้าของของตัวชี้หลังจากการเรียกร้องให้ptrMyFunc


5

เนื่องจากMyFuncไม่ได้เป็นเจ้าของมันจะดีกว่าถ้ามี:

void MyFunc(const A* arg)
{
    assert(arg != nullptr); // or throw ?
    cout << arg->GetVal() << endl;
}

หรือดีกว่า

void MyFunc(const A& arg)
{
    cout << arg.GetVal() << endl;
}

หากคุณต้องการเป็นเจ้าของจริงๆคุณต้องย้ายทรัพยากรของคุณ:

std::unique_ptr<A> ptr = std::make_unique<A>(1234);
MyFunc(std::move(ptr));

หรือส่งต่อการอ้างอิง r-value โดยตรง:

MyFunc(std::make_unique<A>(1234));

std::unique_ptr ไม่มีสำเนาโดยมีวัตถุประสงค์เพื่อรับประกันว่าจะมีเจ้าของเพียงคนเดียว


คำตอบนี้ไม่มีลักษณะการโทรไปยัง MyFunc สำหรับสองกรณีแรกของคุณ ไม่แน่ใจว่าคุณกำลังใช้ptr.get()หรือเพียงแค่ส่งผ่านptrเข้าไปในฟังก์ชั่น?
taylorstine

ลายเซ็นที่ตั้งใจไว้MyFuncสำหรับส่งผ่านการอ้างอิงค่า r คืออะไร?
James Hirschorn

@JamesHirschorn: void MyFunc(A&& arg)ใช้อ้างอิง r-value ...
Jarod42

@ Jarod42 นั่นคือสิ่งที่ผมคาดเดา แต่ด้วยtypedef int A[], MyFunc(std::make_unique<A>(N))ให้รวบรวมข้อผิดพลาด: ข้อผิดพลาด: การเริ่มต้นที่ไม่ถูกต้องของการอ้างอิงชนิด 'int (&&) [] จากการแสดงออกของชนิด 'มาตรฐาน :: _ MakeUniq <int []> :: __ อาร์เรย์'{ aka 'std :: unique_ptr <int [], std :: default_delete <int []>>'} g++ -std=gnu++11ล่าสุดเพียงพอหรือไม่
James Hirschorn

@ Jarod42 ฉันยืนยันความล้มเหลวสำหรับ C ++ 14 ด้วย ideone: ideone.com/YRRT24
James Hirschorn

4

เหตุใดฉันจึงไม่สามารถส่งผ่านunique_ptrเข้าสู่ฟังก์ชันได้

คุณทำได้ แต่ไม่ใช่โดยการคัดลอก - เนื่องจากstd::unique_ptr<>ไม่สามารถคัดลอกได้

แน่นอนว่านี่คือจุดประสงค์หลักของการสร้าง?

เหนือสิ่งอื่นใดstd::unique_ptr<>ออกแบบมาเพื่อระบุความเป็นเจ้าของที่ไม่เหมือนใครอย่างชัดเจน (ตรงข้ามกับstd::shared_ptr<>)

และที่แปลกที่สุดทำไมนี่ถึงเป็นวิธีที่ใช้ได้?

เนื่องจากในกรณีนั้นไม่มีการสร้างสำเนา


ขอบคุณสำหรับคำตอบ. ไม่สนใจคุณช่วยอธิบายได้ไหมว่าทำไมวิธีสุดท้ายในการส่งผ่านไม่ได้ใช้ตัวสร้างการคัดลอก ฉันคิดว่าการใช้คอนสตรัคเตอร์ของ unique_ptr จะสร้างอินสแตนซ์บนสแต็กซึ่งจะถูกคัดลอกไปยังอาร์กิวเมนต์สำหรับ MyFunc () โดยใช้ตัวสร้างการคัดลอก? แม้ว่าฉันจะยอมรับว่าความทรงจำของฉันยังคลุมเครือเล็กน้อยในพื้นที่นี้
user3690202

2
เนื่องจากเป็นค่า rvalue จึงเรียกตัวสร้างการเคลื่อนย้ายแทน แม้ว่าคอมไพเลอร์ของคุณจะปรับให้เหมาะสมที่สุดก็ตาม
Nielk

0

เนื่องจากunique_ptrมีไว้สำหรับความเป็นเจ้าของที่ไม่ซ้ำกันหากคุณต้องการส่งผ่านเป็นอาร์กิวเมนต์ลอง

MyFunc(move(ptr));

แต่หลังจากนั้นสถานะptrในmainจะเป็นnullptrอย่างไร


4
"จะไม่ถูกกำหนด" - ไม่มันจะเป็นโมฆะหรือunique_ptrค่อนข้างไร้ประโยชน์
TC

0

การส่งผ่านstd::unique_ptr<T>เป็นค่าไปยังฟังก์ชันใช้งานไม่ได้เพราะอย่างที่พวกคุณพูดถึงunique_ptrนั้นไม่สามารถคัดลอกได้

อะไรประมาณนี้

std::unique_ptr<T> getSomething()
{
   auto ptr = std::make_unique<T>();
   return ptr;
}

รหัสนี้ใช้งานได้


ถ้ารหัสนั้นใช้งานได้ฉันเดาว่ามันไม่ได้คัดลอก unique_ptr แต่ในความเป็นจริงแล้วใช้ความหมายการย้ายเพื่อย้าย
user3690202

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