T&& (เครื่องหมายคู่คู่) หมายถึงอะไรใน C ++ 11


799

ฉันได้รับการมองเข้าไปในบางส่วนของคุณสมบัติใหม่ของ C ++ 11 T&& varและเป็นหนึ่งฉันได้สังเกตเห็นเป็นเครื่องหมายคู่ในตัวแปรประกาศเช่น

สำหรับการเริ่มต้นสัตว์ร้ายตัวนี้เรียกว่าอะไร? ฉันต้องการให้ Google อนุญาตให้เราค้นหาเครื่องหมายวรรคตอนเช่นนี้

มันหมายความว่าอะไรกันแน่?

จากภาพรวมในครั้งแรกดูเหมือนว่าจะเป็นการอ้างอิงสองครั้ง (เช่นตัวชี้ดับเบิลแบบ C T** var) แต่ฉันมีความลำบากในการนึกถึงกรณีการใช้งานสำหรับสิ่งนั้น


55
ฉันได้เพิ่มสิ่งนี้ลงใน c ++ - faq เนื่องจากฉันแน่ใจว่ามันจะเพิ่มขึ้นในอนาคต
GManNickG

3
คำถามที่เกี่ยวข้องเกี่ยวกับอรรถศาสตร์การย้าย
fredoverflow

41
คุณสามารถค้นหาสิ่งนี้ได้โดยใช้ google คุณจะต้องใส่วลีของคุณในเครื่องหมายคำพูด: google.com/#q="T%26%26 "ตอนนี้มีคำถามของคุณเป็นคำถามแรก :)
sbi

มีคำตอบที่ดีมากและเข้าใจง่ายสำหรับคำถามที่คล้ายกันที่นี่stackoverflow.com/questions/7153991/…
แดเนียล

2
ฉันมีคำถามแบบสแต็คโอเวอร์โฟลว์สามคำถามที่การค้นหายอดนิยมใน Google สำหรับ "c ++ สองพารามิเตอร์แอมเปอร์แซนด์" และคุณเป็นคำถามแรก ดังนั้นคุณไม่จำเป็นต้องใช้เครื่องหมายวรรคตอนสำหรับสิ่งนี้หากคุณสามารถสะกด "พารามิเตอร์แอมเปอร์แซนด์สองพารามิเตอร์"
sergiol

คำตอบ:


668

มันประกาศการอ้างอิง rvalue (เอกสารข้อเสนอมาตรฐาน)

นี่คือข้อมูลเบื้องต้นเกี่ยวกับการอ้างอิงค่า

นี่คือลักษณะที่ยอดเยี่ยมในเชิงลึกที่อ้างอิง rvalue โดยหนึ่งในห้องสมุดมาตรฐานไมโครซอฟท์พัฒนา

ข้อควรระวัง:บทความที่เชื่อมโยงกับ MSDN ("การอ้างอิง Rvalue: C ++ 0x คุณลักษณะใน VC10, ตอนที่ 2") เป็นการแนะนำที่ชัดเจนมากเกี่ยวกับการอ้างอิง Rvalue แต่ทำให้คำสั่งเกี่ยวกับการอ้างอิง Rvalue ที่ครั้งหนึ่งจริงใน C ++ 11 มาตรฐาน แต่ไม่เป็นความจริงสำหรับขั้นตอนสุดท้าย! โดยเฉพาะมันบอกว่าในหลาย ๆ จุดที่การอ้างอิง rvalue สามารถผูกกับ lvalues ​​ซึ่งครั้งหนึ่งเคยเป็นจริง แต่ถูกเปลี่ยน (เช่น int x; int && rrx = x; ไม่รวบรวมใน GCC) - drewbarbs 13 ก.ค. 14 เวลา 16:12

ความแตกต่างที่ใหญ่ที่สุดระหว่างการอ้างอิง C ++ 03 (ปัจจุบันเรียกว่าการอ้างอิง lvalue ใน C ++ 11) คือมันสามารถผูกเข้ากับ rvalue แบบชั่วคราวโดยไม่ต้องเป็น const ดังนั้นไวยากรณ์นี้จึงถูกต้องตามกฎหมาย:

T&& r = T();

การอ้างอิง rvalue เป็นหลักให้สำหรับต่อไปนี้:

ย้ายหมาย ตัวสร้างการย้ายและตัวดำเนินการกำหนดการย้ายในขณะนี้สามารถกำหนดได้โดยใช้การอ้างอิง rvalue แทนการอ้างอิง const-lvalue ปกติ ฟังก์ชั่นย้ายเช่นสำเนายกเว้นมันไม่จำเป็นต้องรักษาแหล่งที่มาไม่เปลี่ยนแปลง ในความเป็นจริงมันมักจะแก้ไขแหล่งที่มาว่ามันไม่ได้เป็นเจ้าของทรัพยากรที่ย้าย นี่เป็นสิ่งที่ยอดเยี่ยมสำหรับการกำจัดสำเนาที่ไม่เกี่ยวข้องโดยเฉพาะอย่างยิ่งในการใช้งานไลบรารีมาตรฐาน

ตัวอย่างเช่นตัวสร้างสำเนาอาจมีลักษณะเช่นนี้:

foo(foo const& other)
{
    this->length = other.length;
    this->ptr = new int[other.length];
    copy(other.ptr, other.ptr + other.length, this->ptr);
}

หากคอนสตรัคเตอร์นี้ผ่านไปชั่วคราวสำเนาจะไม่จำเป็นเพราะเรารู้ว่าจะถูกทำลายชั่วคราว เหตุใดจึงไม่ใช้ทรัพยากรที่จัดสรรไว้ชั่วคราวแล้ว ใน C ++ 03 ไม่มีทางที่จะป้องกันการคัดลอกเนื่องจากเราไม่สามารถระบุได้ว่าเราถูกส่งผ่านชั่วคราว ใน C ++ 11 เราสามารถโหลดตัวสร้างการย้ายมากเกินไป:

foo(foo&& other)
{
   this->length = other.length;
   this->ptr = other.ptr;
   other.length = 0;
   other.ptr = nullptr;
}

สังเกตเห็นความแตกต่างใหญ่ที่นี่: ตัวสร้างการย้ายปรับเปลี่ยนอาร์กิวเมนต์ของจริง สิ่งนี้จะมีประสิทธิภาพ "ย้าย" ชั่วคราวลงในวัตถุที่กำลังสร้างจึงกำจัดสำเนาที่ไม่จำเป็น

ตัวสร้างการย้ายจะถูกใช้สำหรับชั่วขณะและสำหรับการอ้างอิงไม่ใช่ lvalue แบบ const ที่แปลงอย่างชัดเจนเป็นการอ้างอิงแบบ rvalue โดยใช้std::moveฟังก์ชัน (เพิ่งทำการแปลง) รหัสต่อไปนี้ทั้งสองเรียกตัวสร้างการย้ายสำหรับf1และf2:

foo f1((foo())); // Move a temporary into f1; temporary becomes "empty"
foo f2 = std::move(f1); // Move f1 into f2; f1 is now "empty"

การส่งต่อที่สมบูรณ์แบบ การอ้างอิงค่า rvalue ทำให้เราสามารถส่งต่อข้อโต้แย้งอย่างเหมาะสมสำหรับฟังก์ชั่น templated ยกตัวอย่างฟังก์ชั่นโรงงานนี้:

template <typename T, typename A1>
std::unique_ptr<T> factory(A1& a1)
{
    return std::unique_ptr<T>(new T(a1));
}

ถ้าเราเรียกว่าfactory<foo>(5)อาร์กิวเมนต์จะอนุมานได้ว่าเป็นที่int&ซึ่งจะไม่ผูกกับตัวอักษร 5 แม้ว่าfoo's intคอนสตรัคใช้เวลา ทีนี้เราสามารถใช้แทนได้A1 const&แต่ถ้าfooใช้อาร์กิวเมนต์ตัวสร้างโดยการอ้างอิงที่ไม่ใช่ const? ที่จะทำให้การทำงานของโรงงานทั่วไปอย่างแท้จริงเราจะต้องเกินโรงงานและA1& A1 const&อาจเป็นเรื่องปกติหากโรงงานใช้พารามิเตอร์ 1 ชนิด แต่พารามิเตอร์เพิ่มเติมแต่ละประเภทจะทวีคูณการโอเวอร์โหลดที่จำเป็นตาม 2 ซึ่งไม่สามารถทำได้อย่างรวดเร็ว

การอ้างอิง rvalue แก้ไขปัญหานี้โดยอนุญาตให้ไลบรารีมาตรฐานกำหนดstd::forwardฟังก์ชันที่สามารถส่งต่อการอ้างอิง lvalue / rvalue ได้อย่างถูกต้อง สำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีการstd::forwardทำงานให้ดูคำตอบที่ยอดเยี่ยมนี้

สิ่งนี้ทำให้เราสามารถกำหนดฟังก์ชั่นจากโรงงานดังนี้:

template <typename T, typename A1>
std::unique_ptr<T> factory(A1&& a1)
{
    return std::unique_ptr<T>(new T(std::forward<A1>(a1)));
}

ตอนนี้อาร์กิวเมนต์ rvalue / lvalue-ness ของอาร์กิวเมนต์จะถูกรักษาไว้เมื่อส่งผ่านไปยังตัวTสร้างของ นั่นหมายความว่าถ้าโรงงานถูกเรียกด้วยค่า rvalue ตัวTสร้างของจะถูกเรียกด้วยค่า rvalue ถ้าโรงงานถูกเรียกด้วย lvalue คอนสตรัคเตอร์Tจะถูกเรียกด้วย lvalue ฟังก์ชั่นที่ได้รับการปรับปรุงจากโรงงานทำงานได้เนื่องจากกฎพิเศษหนึ่งข้อ:

เมื่อประเภทพารามิเตอร์ฟังก์ชั่นเป็นของรูปแบบT&&ที่Tเป็นพารามิเตอร์แม่แบบและฟังก์ชั่นการโต้แย้งเป็น lvalue ประเภทAประเภทที่A&ใช้สำหรับการหักอาร์กิวเมนต์แม่แบบ

ดังนั้นเราสามารถใช้โรงงานเช่นนี้:

auto p1 = factory<foo>(foo()); // calls foo(foo&&)
auto p2 = factory<foo>(*p1);   // calls foo(foo const&)

คุณสมบัติอ้างอิง rvalue ที่สำคัญ :

  • สำหรับการแก้ปัญหาเกินlvalues ชอบผูกพันกับการอ้างอิง lvalue และ rvalues ชอบผูกพันกับการอ้างอิง ดังนั้นทำไม Tem Tem ต้องการเรียกตัวสร้างการย้าย / ผู้ประกอบการที่ได้รับมอบหมายย้ายมากกว่าผู้ประกอบการคัดลอก / ผู้ประกอบการได้รับมอบหมาย
  • อ้างอิง rvalue โดยปริยายจะผูกกับ rvalues และชั่วคราวที่มีผลมาจากการแปลงนัย นั่นfloat f = 0f; int&& i = f;คือการก่อตัวที่ดีเพราะเป็นปริยายแปลงลอยเพราะ int; การอ้างอิงจะเป็นการชั่วคราวที่เป็นผลลัพธ์ของการแปลง
  • การอ้างอิง rvalue ที่ระบุชื่อคือ lvalues การอ้างอิงค่า rvalue ที่ไม่มีชื่อนั้นเป็น rvalues นี่เป็นสิ่งสำคัญที่จะต้องเข้าใจว่าเหตุใดการstd::moveโทรจึงมีความจำเป็นใน:foo&& r = foo(); foo f = std::move(r);

65
+1 สำหรับNamed rvalue references are lvalues. Unnamed rvalue references are rvalues.; ฉันพยายามดิ้นรนที่จะเข้าใจว่าทำไมผู้คนถึงทำT &&t; std::move(t);แคชเชียร์เป็นเวลานานและไม่ชอบ
legends2k

@ MaximYegorushkin: ในตัวอย่างนั้น r มีผลผูกพันกับ rvalue บริสุทธิ์ (ชั่วคราว) และดังนั้นชั่วคราวควรมีขอบเขตอายุการใช้งานของการขยายไม่?
Peter Huene

@ PeterHuene ฉันรับมันกลับมาการอ้างอิง r-value ช่วยยืดอายุการใช้งานของชั่วคราว
Maxim Egorushkin

32
ข้อควรระวัง : บทความการเชื่อมโยงใน MSDN ( "rvalue อ้างอิง: c ++ 0x คุณสมบัติใน VC10, Part 2") คือการแนะนำที่ชัดเจนมากที่จะอ้างอิง rvalue, แต่ทำให้งบเกี่ยวกับการอ้างอิง rvalue ที่มีครั้งเดียวที่แท้จริงในร่าง C ++ 11 มาตรฐาน แต่ไม่เป็นความจริงสำหรับขั้นตอนสุดท้าย! โดยเฉพาะมันบอกว่าในหลาย ๆ จุดที่การอ้างอิง rvalue สามารถผูกกับ lvalues ​​ซึ่งครั้งหนึ่งเคยเป็นจริง แต่ถูกเปลี่ยน (เช่นint x; int &&rrx = x; ไม่คอมไพล์ใน GCC อีกต่อไป )
drewbarbs

@PeterHuene ในตัวอย่างด้านบนไม่typename identity<T>::type& aเท่ากับT&หรือ?
ibp73

81

มันหมายถึงการอ้างอิงค่า การอ้างอิงค่า Rvalue จะผูกกับวัตถุชั่วคราวเท่านั้นเว้นแต่จะสร้างเป็นอย่างอื่นอย่างชัดเจน พวกเขาจะใช้ในการทำให้วัตถุมีประสิทธิภาพมากขึ้นภายใต้สถานการณ์บางอย่างและเพื่ออำนวยความสะดวกที่เรียกว่าการส่งต่อที่สมบูรณ์แบบซึ่งช่วยลดความซับซ้อนของรหัสแม่แบบ

ใน C ++ 03 คุณไม่สามารถแยกความแตกต่างระหว่างสำเนาของ lvalue ที่ไม่แน่นอนและ rvalue

std::string s;
std::string another(s);           // calls std::string(const std::string&);
std::string more(std::string(s)); // calls std::string(const std::string&);

ใน C ++ 0x นี่ไม่ใช่กรณี

std::string s;
std::string another(s);           // calls std::string(const std::string&);
std::string more(std::string(s)); // calls std::string(std::string&&);

พิจารณาการใช้งานที่อยู่เบื้องหลังตัวสร้างเหล่านี้ ในกรณีแรกสตริงต้องทำการคัดลอกเพื่อเก็บค่าซีแมนทิกส์ซึ่งเกี่ยวข้องกับการจัดสรรฮีปใหม่ อย่างไรก็ตามในกรณีที่สองเรารู้ล่วงหน้าว่าวัตถุที่ผ่านเข้าไปในตัวสร้างของเรานั้นถึงกำหนดทันทีเนื่องจากการทำลายและมันไม่จำเป็นต้องคงอยู่ เราสามารถเปลี่ยนพอยน์เตอร์ภายในได้อย่างมีประสิทธิภาพและไม่ทำการคัดลอกใด ๆ เลยในสถานการณ์นี้ซึ่งมีประสิทธิภาพมากกว่ามาก ความหมายของการย้ายมีประโยชน์ต่อคลาสที่มีราคาแพงหรือห้ามคัดลอกทรัพยากรที่อ้างอิงภายใน พิจารณากรณีของstd::unique_ptr- ขณะนี้ที่ชั้นเรียนของเราสามารถแยกความแตกต่างระหว่างขมับและไม่ใช่ขมับเราสามารถทำให้ความหมายย้ายทำงานอย่างถูกต้องเพื่อให้unique_ptrไม่สามารถคัดลอก แต่สามารถย้ายซึ่งหมายความว่าstd::unique_ptrสามารถจัดเก็บอย่างถูกกฎหมายในคอนเทนเนอร์มาตรฐานการเรียงลำดับ ฯลฯ ขณะที่ C ++ 03 std::auto_ptrไม่สามารถทำได้

ตอนนี้เราพิจารณาการใช้การอ้างอิง rvalue อื่น ๆ - การส่งต่อที่สมบูรณ์แบบ พิจารณาคำถามที่มีผลผูกพันการอ้างอิงถึงการอ้างอิง

std::string s;
std::string& ref = s;
(std::string&)& anotherref = ref; // usually expressed via template

ไม่สามารถจำสิ่งที่ C ++ 03 พูดเกี่ยวกับสิ่งนี้ได้ แต่ใน C ++ 0x ประเภทผลลัพธ์เมื่อจัดการกับการอ้างอิง rvalue นั้นมีความสำคัญ การอ้างอิง rvalue กับประเภท T โดยที่ T เป็นประเภทการอ้างอิงจะกลายเป็นการอ้างอิงประเภท T

(std::string&)&& ref // ref is std::string&
(const std::string&)&& ref // ref is const std::string&
(std::string&&)&& ref // ref is std::string&&
(const std::string&&)&& ref // ref is const std::string&&

พิจารณาฟังก์ชั่นแม่แบบที่ง่ายที่สุดและต่ำสุด ใน C ++ 03 คุณต้องโอเวอร์โหลดชุดค่าผสมทั้งสี่รายการของค่าคงที่และค่าที่ไม่ใช่ค่าปรับด้วยตนเอง ใน C ++ 0x เป็นเพียงโอเวอร์โหลดเดียว เมื่อรวมกับเทมเพลต variadic สิ่งนี้ทำให้การส่งต่อสมบูรณ์แบบ

template<typename A, typename B> auto min(A&& aref, B&& bref) {
    // for example, if you pass a const std::string& as first argument,
    // then A becomes const std::string& and by extension, aref becomes
    // const std::string&, completely maintaining it's type information.
    if (std::forward<A>(aref) < std::forward<B>(bref))
        return std::forward<A>(aref);
    else
        return std::forward<B>(bref);
}

ฉันออกจากการหักค่าส่งคืนเนื่องจากฉันจำไม่ได้ว่ามันทำออกมาได้อย่างไร แต่นาทีนั้นสามารถยอมรับการรวมกันของ lvalues, rvalues, const lvalues ​​ได้


เหตุผลที่คุณใช้std::forward<A>(aref) < std::forward<B>(bref)? และผมไม่คิดว่าคำนิยามนี้จะถูกต้องเมื่อคุณพยายามไปข้างหน้าและint& float&ดีกว่าปล่อยแม่แบบฟอร์มชนิดหนึ่ง
Yankes

25

ระยะสำหรับ The T&& เมื่อใช้กับชนิดหัก (เช่นสำหรับการส่งต่อที่สมบูรณ์แบบ) เป็นที่รู้จักกันในชื่อเรียกขานเป็นอ้างอิงส่งต่อ คำว่า "การอ้างอิงสากล" ประกาศเกียรติคุณจาก Scott Meyers ในบทความนี้แต่เปลี่ยนไปในภายหลัง

นั่นเป็นเพราะมันอาจเป็นค่า r-value หรือ l-value

ตัวอย่างคือ:

// template
template<class T> foo(T&& t) { ... }

// auto
auto&& t = ...;

// typedef
typedef ... T;
T&& t = ...;

// decltype
decltype(...)&& t = ...;

การสนทนาเพิ่มเติมสามารถพบได้ในคำตอบสำหรับ: ไวยากรณ์สำหรับการอ้างอิงสากล


14

การอ้างอิงค่า rvalue เป็นประเภทที่ทำงานเหมือนกับการอ้างอิง X & ปกติโดยมีข้อยกเว้นหลายประการ สิ่งที่สำคัญที่สุดคือเมื่อมีการทำงานเกินความละเอียด lvalues ​​ชอบการอ้างอิง lvalue แบบเก่าในขณะที่ rvalues ​​ชอบการอ้างอิง rvalue ใหม่:

void foo(X& x);  // lvalue reference overload
void foo(X&& x); // rvalue reference overload

X x;
X foobar();

foo(x);        // argument is lvalue: calls foo(X&)
foo(foobar()); // argument is rvalue: calls foo(X&&)

ค่า rvalue คืออะไร? ทุกสิ่งที่ไม่ใช่ค่า lvalue lvalue เป็นนิพจน์ที่อ้างถึงตำแหน่งหน่วยความจำและช่วยให้เราสามารถระบุที่อยู่ของตำแหน่งหน่วยความจำนั้นผ่านทาง & โอเปอเรเตอร์

มันง่ายกว่าที่จะเข้าใจก่อนสิ่งที่ค่า rvalues ​​ทำได้ด้วยตัวอย่าง:

 #include <cstring>
 class Sample {
  int *ptr; // large block of memory
  int size;
 public:
  Sample(int sz=0) : ptr{sz != 0 ? new int[sz] : nullptr}, size{sz} 
  {
     if (ptr != nullptr) memset(ptr, 0, sz);
  }
  // copy constructor that takes lvalue 
  Sample(const Sample& s) : ptr{s.size != 0 ? new int[s.size] :\
      nullptr}, size{s.size}
  {
     if (ptr != nullptr) memcpy(ptr, s.ptr, s.size);
     std::cout << "copy constructor called on lvalue\n";
  }

  // move constructor that take rvalue
  Sample(Sample&& s) 
  {  // steal s's resources
     ptr = s.ptr;
     size = s.size;        
     s.ptr = nullptr; // destructive write
     s.size = 0;
     cout << "Move constructor called on rvalue." << std::endl;
  }    
  // normal copy assignment operator taking lvalue
  Sample& operator=(const Sample& s)
  {
   if(this != &s) {
      delete [] ptr; // free current pointer
      size = s.size;

      if (size != 0) {
        ptr = new int[s.size];
        memcpy(ptr, s.ptr, s.size);
      } else 
         ptr = nullptr;
     }
     cout << "Copy Assignment called on lvalue." << std::endl;
     return *this;
  }    
 // overloaded move assignment operator taking rvalue
 Sample& operator=(Sample&& lhs)
 {
   if(this != &s) {
      delete [] ptr; //don't let ptr be orphaned 
      ptr = lhs.ptr;   //but now "steal" lhs, don't clone it.
      size = lhs.size; 
      lhs.ptr = nullptr; // lhs's new "stolen" state
      lhs.size = 0;
   }
   cout << "Move Assignment called on rvalue" << std::endl;
   return *this;
 }
//...snip
};     

ตัวสร้างและตัวดำเนินการกำหนดได้รับการโหลดมากเกินไปกับรุ่นที่ใช้การอ้างอิง rvalue การอ้างอิง Rvalue อนุญาตให้ฟังก์ชันแยกสาขาในเวลาคอมไพล์ (ผ่านการแก้ปัญหาการโอเวอร์โหลด) กับเงื่อนไข "ฉันถูกเรียกบน lvalue หรือ rvalue หรือไม่" สิ่งนี้ทำให้เราสามารถสร้างตัวสร้างและตัวดำเนินการกำหนดค่าที่มีประสิทธิภาพมากกว่าด้านบนที่ย้ายทรัพยากรแทนที่จะคัดลอก

คอมไพเลอร์แยกสาขาโดยอัตโนมัติในเวลารวบรวม (ขึ้นอยู่กับว่ามันถูกเรียกใช้สำหรับ lvalue หรือ rvalue) เลือกว่าตัวสร้างการย้ายหรือตัวดำเนินการกำหนดย้ายควรถูกเรียก

ข้อสรุป: การอ้างอิงค่า rvalue อนุญาตให้มีการย้ายความหมาย (และการส่งต่อที่สมบูรณ์แบบซึ่งกล่าวถึงในลิงก์บทความด้านล่าง)

การปฏิบัติที่ง่ายต่อการเข้าใจตัวอย่างหนึ่งคือแม่แบบเรียนมาตรฐาน :: unique_ptr เนื่องจาก unique_ptr รักษาความเป็นเจ้าของเอกสิทธิ์ของตัวชี้ดิบพื้นฐานของมันจึงไม่สามารถคัดลอก unique_ptr ของ นั่นจะเป็นการละเมิดความไม่เปลี่ยนแปลงในการเป็นเจ้าของ ดังนั้นพวกเขาจึงไม่มีตัวสร้างสำเนา แต่พวกเขามีตัวสร้างการเคลื่อนย้าย:

template<class T> class unique_ptr {
  //...snip
 unique_ptr(unique_ptr&& __u) noexcept; // move constructor
};

 std::unique_ptr<int[] pt1{new int[10]};  
 std::unique_ptr<int[]> ptr2{ptr1};// compile error: no copy ctor.  

 // So we must first cast ptr1 to an rvalue 
 std::unique_ptr<int[]> ptr2{std::move(ptr1)};  

std::unique_ptr<int[]> TakeOwnershipAndAlter(std::unique_ptr<int[]> param,\
 int size)      
{
  for (auto i = 0; i < size; ++i) {
     param[i] += 10;
  }
  return param; // implicitly calls unique_ptr(unique_ptr&&)
}

// Now use function     
unique_ptr<int[]> ptr{new int[10]};

// first cast ptr from lvalue to rvalue
unique_ptr<int[]> new_owner = TakeOwnershipAndAlter(\
           static_cast<unique_ptr<int[]>&&>(ptr), 10);

cout << "output:\n";

for(auto i = 0; i< 10; ++i) {
   cout << new_owner[i] << ", ";
}

output:
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 

static_cast<unique_ptr<int[]>&&>(ptr)มักจะทำโดยใช้std :: move

// first cast ptr from lvalue to rvalue
unique_ptr<int[]> new_owner = TakeOwnershipAndAlter(std::move(ptr),0);

บทความดีอธิบายทั้งหมดนี้และอื่น ๆ (เช่นวิธี rvalues อนุญาตให้มีการส่งต่อที่สมบูรณ์แบบและสิ่งที่หมายถึง) ที่มีจำนวนมากตัวอย่างที่ดีคือโทมัสเบกเกอร์c ++ rvalue อ้างอิงอธิบาย โพสต์นี้ต้องอาศัยบทความของเขาเป็นอย่างมาก

การแนะนำที่สั้นกว่าคือการแนะนำโดยย่อเกี่ยวกับการอ้างอิง Rvalue โดย Stroutrup และ อัล


ไม่ใช่ว่าตัวสร้างSample(const Sample& s)การคัดลอกจะต้องคัดลอกเนื้อหาด้วยหรือไม่ คำถามเดียวกันสำหรับ 'ผู้ดำเนินการกำหนดค่าการคัดลอก'
K.Karamazen

ใช่คุณถูก. ฉันคัดลอกหน่วยความจำไม่ได้ ตัวสร้างสำเนาและตัวดำเนินการกำหนดค่าควรทำ memcpy (ptr, s.ptr, size) หลังจากทดสอบขนาดนั้น! = 0 และตัวสร้างเริ่มต้นควรทำ memset (ptr, 0, size) ถ้าขนาด! = 0.
kurt krueckeberg

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