กฎการยกเลิก Iterator


543

กฎการตรวจสอบความถูกต้องของตัววนซ้ำสำหรับคอนเทนเนอร์ C ++ มีอะไรบ้าง

โดยเฉพาะอย่างยิ่งในรูปแบบรายการสรุป

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


คำตอบควรอยู่ในรูปแบบเดียวกับคำตอบของคุณหรือไม่
PW

@PW IMO ที่ต้องการความสมมาตร แต่ฉันไม่สามารถบังคับได้: P
Lightness Races ใน Orbit

แล้ว c ++ 20 ล่ะ
วอลเตอร์

1
@Walter ยังไม่มีอยู่;)
Lightness Races ใน Orbit

คำถามนี้คือการอ้าง Leela จาก Futurama จากวัยโง่และควรปล่อยให้เปิดไว้
Roman Luštrik

คำตอบ:


110

C ++ 17 (การอ้างอิงทั้งหมดมาจากร่างการทำงานขั้นสุดท้ายของ CPP17 - n4659 )


การแทรก

ภาชนะบรรจุลำดับ

  • vector: ฟังก์ชั่นinsert, emplace_back, emplace, push_backสาเหตุการจัดสรรถ้าขนาดใหม่ที่มีค่ามากกว่าความจุเก่า การจัดสรรใหม่จะทำให้การอ้างอิงพอยน์เตอร์และตัววนซ้ำทั้งหมดอ้างถึงองค์ประกอบในลำดับ หากไม่มีการจัดสรรใหม่เกิดขึ้นตัววนซ้ำและการอ้างอิงทั้งหมดก่อนจุดแทรกจะยังคงใช้ได้ [26.3.11.5/1/1]
    เกี่ยวกับreserveฟังก์ชั่นการจัดสรรใหม่จะยกเลิกการอ้างอิงตัวชี้และตัววนซ้ำทั้งหมดที่อ้างถึงองค์ประกอบตามลำดับ ไม่มีการจัดสรรจะเกิดขึ้นในช่วงการแทรกที่เกิดขึ้นหลังจากการโทรไปจนกว่าจะถึงเวลาเมื่อมีการแทรกจะทำให้ขนาดของเวกเตอร์มากขึ้นกว่าค่าของreserve() capacity()[26.3.11.3/6]

  • deque: การแทรกที่กึ่งกลางของ deque จะยกเลิกการวนซ้ำทั้งหมดและการอ้างอิงไปยังองค์ประกอบของ deque การแทรกที่ปลายทั้งสองของ deque จะยกเลิกโมเดอเรเตอร์ทั้งหมดไปยัง deque แต่ไม่มีผลต่อความถูกต้องของการอ้างอิงถึงองค์ประกอบของ deque [26.3.8.4/1]

  • list: ไม่ส่งผลกระทบต่อความถูกต้องของตัววนซ้ำและการอ้างอิง หากมีการโยนข้อยกเว้นจะไม่มีผลกระทบใด ๆ [26.3.10.4/1] , , , , , ฟังก์ชั่นจะอยู่ภายใต้กฎนี้
    insertemplace_frontemplace_backemplacepush_frontpush_back

  • forward_list: ไม่มีการโอเวอร์โหลดของinsert_afterจะไม่มีผลต่อความถูกต้องของตัววนซ้ำและการอ้างอิง [26.3.9.5/1]

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

ภาชนะบรรจุที่เกี่ยวข้อง

  • All Associative Containers: insertและemplaceสมาชิกจะไม่ส่งผลกระทบต่อความถูกต้องของตัววนซ้ำและการอ้างอิงไปยังคอนเทนเนอร์ [26.2.6 / 9]

ภาชนะบรรจุที่ไม่ได้เรียงลำดับ

  • All Unordered Associative Containers: การทำซ้ำตัวตรวจสอบความถูกต้องจะทำให้ตัววนซ้ำใช้ไม่ได้การเปลี่ยนแปลงการเรียงลำดับระหว่างองค์ประกอบและการเปลี่ยนแปลงที่องค์ประกอบบุ้งกี๋ปรากฏขึ้น [26.2.7 / 9] และสมาชิกจะไม่ส่งผลกระทบต่อความถูกต้องของการอ้างอิงถึงองค์ประกอบภาชนะ แต่อาจทำให้การ iterators ทั้งหมดกับภาชนะบรรจุ [26.2.7 / 14] และสมาชิกจะไม่ส่งผลกระทบต่อความถูกต้องของ iterators หากที่เป็นจำนวนขององค์ประกอบในภาชนะก่อนที่จะดำเนินการแทรกที่เป็นจำนวนขององค์ประกอบแทรกที่เป็นคอนเทนเนอร์นับถังและเป็น โหลดตัวประกอบสูงสุดของตู้คอนเทนเนอร์ [26.2.7 / 15]
    insertemplace
    insertemplace(N+n) <= z * BNnBz

  • All Unordered Associative Containers: ในกรณีของการดำเนินการผสาน (เช่นa.merge(a2)) ตัววนซ้ำอ้างอิงองค์ประกอบที่ถูกถ่ายโอนและตัววนซ้ำทั้งหมดที่อ้างถึงaจะไม่ถูกต้อง แต่ตัววนซ้ำไปยังองค์ประกอบที่เหลืออยู่a2จะยังคงใช้ได้ (ตารางที่ 91 - ข้อกำหนดคอนเทนเนอร์เชื่อมโยงที่ไม่ได้เรียงลำดับ)

ตัวแปลงคอนเทนเนอร์

  • stack: สืบทอดมาจากคอนเทนเนอร์พื้นฐาน
  • queue: สืบทอดมาจากคอนเทนเนอร์พื้นฐาน
  • priority_queue: สืบทอดมาจากคอนเทนเนอร์พื้นฐาน

การลบออก

ภาชนะบรรจุลำดับ

  • vector: ฟังก์ชั่นeraseและตัวทำpop_backซ้ำที่ไม่ถูกต้องและการอ้างอิงในหรือหลังจุดลบ [26.3.11.5/3]

  • deque: การดำเนินการลบที่ลบองค์ประกอบสุดท้ายของdequeโมฆะจะใช้เฉพาะตัววนซ้ำในอดีตและตัววนซ้ำทั้งหมดและการอ้างอิงถึงองค์ประกอบที่ถูกลบ การดำเนินการลบที่ลบองค์ประกอบแรกของdequeแต่ไม่ใช่องค์ประกอบสุดท้ายจะยกเลิกการใช้งานเฉพาะตัววนซ้ำและการอ้างอิงไปยังองค์ประกอบที่ถูกลบ การดำเนินการลบที่จะลบค่าองค์ประกอบแรกหรือองค์ประกอบสุดท้ายของdequeเลิก iterator ที่ผ่านมา-the-end และ iterators dequeทั้งหมดและการอ้างอิงถึงองค์ประกอบทั้งหมดของ [หมายเหตุ: pop_frontและpop_backกำลังลบการดำเนินการ - บันทึกท้าย] [26.3.8.4/4]

  • list: ยกเลิกการทำซ้ำเฉพาะการวนซ้ำและการอ้างอิงองค์ประกอบที่ถูกลบ [26.3.10.4/3] นี้นำไปใช้erase, pop_front, pop_back, clearฟังก์ชั่น
    removeและremove_ifฟังก์ชั่นสมาชิก: ลบองค์ประกอบทั้งหมดในรายการที่อ้างอิงโดยตัววนซ้ำรายการiซึ่งมีเงื่อนไขดังต่อไปนี้: *i == value, pred(*i) != false. เลิกใช้เฉพาะตัววนซ้ำและการอ้างอิงองค์ประกอบที่ถูกลบ [26.3.10.5/15]
    uniqueฟังก์ชั่นสมาชิก - ลบทั้งหมดยกเว้นองค์ประกอบแรกจากทุกกลุ่มที่ต่อเนื่องขององค์ประกอบที่เท่าเทียมกันที่อ้างถึงโดย iterator iในช่วง[first + 1, last)ที่*i == *(i-1)(สำหรับรุ่นที่ไม่ซ้ำกันโดยไม่มีข้อโต้แย้ง) หรือpred(*i, *(i - 1))(สำหรับเวอร์ชันที่ไม่ซ้ำกับอาร์กิวเมนต์ของกริยา) ถือ ยกเลิกการทำซ้ำเฉพาะการวนซ้ำและการอ้างอิงถึงองค์ประกอบที่ถูกลบ [26.3.10.5/19]

  • forward_list: erase_afterจะทำให้การวนซ้ำและการอ้างอิงองค์ประกอบที่ลบเป็นโมฆะเท่านั้น [26.3.9.5/1]
    removeและremove_ifฟังก์ชั่นสมาชิก - ลบองค์ประกอบทั้งหมดในรายการที่อ้างอิงโดย list iterator i ซึ่งเงื่อนไขดังต่อไปนี้ถือ: *i == value(สำหรับremove()), pred(*i)เป็นจริง (สำหรับremove_if()) ยกเลิกการทำซ้ำเฉพาะการวนซ้ำและการอ้างอิงถึงองค์ประกอบที่ถูกลบ [26.3.9.6/12]
    uniqueฟังก์ชั่นสมาชิก - ลบทั้งหมดยกเว้นองค์ประกอบแรกจากทุกกลุ่มที่ต่อเนื่องขององค์ประกอบที่เท่าเทียมกันที่อ้างถึงโดย iterator ฉันในช่วง [แรก + 1, สุดท้าย) ซึ่ง*i == *(i-1)(สำหรับรุ่นที่ไม่มีข้อโต้แย้ง) หรือpred(*i, *(i - 1))(สำหรับรุ่นที่มีภาคแสดง อาร์กิวเมนต์) ถือ ยกเลิกการทำซ้ำเฉพาะการวนซ้ำและการอ้างอิงถึงองค์ประกอบที่ถูกลบ [26.3.9.6/16]

  • All Sequence Containers: clearยกเลิกการอ้างอิงพอยน์เตอร์และตัววนซ้ำทั้งหมดที่อ้างถึงองค์ประกอบของ a และอาจทำให้โมเดอเรเตอร์ในอดีตสิ้นสุดลง (ตารางที่ 87 - ข้อกำหนดของลำดับคอนเทนเนอร์) แต่สำหรับforward_list, clearไม่ได้ทำให้ iterators ผ่านไปสิ้น [26.3.9.5/32]

  • All Sequence Containers: assignยกเลิกการอ้างอิงพอยน์เตอร์และตัววนซ้ำทั้งหมดที่อ้างถึงองค์ประกอบของคอนเทนเนอร์ สำหรับvectorและdequeยังทำให้โมเดอเรเตอร์ในอดีตใช้ไม่ได้อีกด้วย (ตารางที่ 87 - ข้อกำหนดคอนเทนเนอร์ของลำดับ)

ภาชนะบรรจุที่เกี่ยวข้อง

  • All Associative Containers: eraseสมาชิกจะต้องยกเลิกการทำซ้ำเท่านั้นและการอ้างอิงถึงองค์ประกอบที่ถูกลบ [26.2.6 / 9]

  • All Associative Containers: extractสมาชิกใช้การวนซ้ำเฉพาะอิลิเมนต์ที่ลบออกเท่านั้น พอยน์เตอร์และการอ้างอิงถึงองค์ประกอบที่ถูกลบยังคงใช้ได้ [26.2.6 / 10]

ตัวแปลงคอนเทนเนอร์

  • stack: สืบทอดมาจากคอนเทนเนอร์พื้นฐาน
  • queue: สืบทอดมาจากคอนเทนเนอร์พื้นฐาน
  • priority_queue: สืบทอดมาจากคอนเทนเนอร์พื้นฐาน

ข้อกำหนดภาชนะทั่วไปที่เกี่ยวข้องกับการตรวจสอบความถูกต้องตัววนซ้ำ:

  • เว้นแต่จะระบุไว้เป็นอย่างอื่น (อย่างชัดเจนหรือโดยการกำหนดฟังก์ชั่นในแง่ของฟังก์ชั่นอื่น ๆ ) การเรียกใช้ฟังก์ชั่นสมาชิกตู้คอนเทนเนอร์หรือผ่านภาชนะที่เป็นข้อโต้แย้งไปยังฟังก์ชั่นห้องสมุดจะต้องไม่ทำให้ซ้ำ iterators . [26.2.1 / 12]

  • ไม่มีswap()ฟังก์ชั่นทำให้การอ้างอิงพอยน์เตอร์หรือตัววนซ้ำใด ๆ ที่อ้างถึงองค์ประกอบของคอนเทนเนอร์ถูกสลับ [หมายเหตุ: ตัวสิ้นสุด () ตัววนซ้ำไม่ได้อ้างอิงองค์ประกอบใด ๆ ดังนั้นจึงอาจใช้การไม่ได้ - บันทึกย่อ] [26.2.1 / (11.6)]

เป็นตัวอย่างของข้อกำหนดข้างต้น:

  • transformอัลกอริทึม: opและbinary_opฟังก์ชั่นจะต้องไม่ทำให้การวนซ้ำหรือการแทนที่ย่อยเป็นโมฆะหรือแก้ไของค์ประกอบในช่วง [28.6.4 / 1]

  • accumulateอัลกอริทึม: ในช่วง [แรก, สุดท้าย], binary_opจะต้องไม่แก้ไของค์ประกอบหรือทำให้ตัววนซ้ำหรือช่วงย่อยไม่ถูกต้อง [29.8.2 / 1]

  • reduceอัลกอริทึม: binary_op จะไม่ทำให้ตัววนซ้ำหรือการแทนที่ไม่ถูกต้องหรือแก้ไของค์ประกอบในช่วง [แรก, สุดท้าย] [29.8.3 / 5]

และอื่น ๆ ...


7
โอ้ PW คุณฮีโร่!
การแข่งขัน Lightness ใน Orbit

2
@LightnessRacesinOrbit: พยายามทำตามรูปแบบคำตอบดั้งเดิมของคุณ :)
PW

1
เราสามารถมีรายชื่อเพื่อได้std::stringหรือไม่? ฉันคิดว่ามันแตกต่างจากstd::vectorSSO
sp2danny

1
@ sp2danny: เนื่องจาก SSO stringล้มเหลวข้อกำหนดทั่วไปที่สองที่ระบุไว้ข้างต้น ดังนั้นฉันจึงไม่รวมมัน พยายามที่จะยึดติดกับรูปแบบเดียวกันของรายการคำถามที่พบบ่อยก่อนหน้านี้
PW

@LightnessRaceswithMonica ขอบคุณมากสำหรับการทำงานหนัก ฉันมีคำถามทำให้ฉันสับสนหลายวัน "โมฆะ" หมายถึงอะไรในบริบทเหล่านี้? มันหมายความว่า"invalidated" can mean "no longer points to what it used to", not just "may not point to any valid element"เป็น @Marshall โคลว์อธิบายไว้ในคำตอบ ? หรือมันเป็นเพียงแค่ระบุว่ามีเพียง 1 ใน 2 เงื่อนไขเท่านั้น
Rick

410

C ++ 03 (แหล่งที่มา: กฎการยกเลิก Iterator (C ++ 03) )


การแทรก

ภาชนะบรรจุลำดับ

  • vector: ตัววนซ้ำทั้งหมดและการอ้างอิงก่อนจุดแทรกจะไม่ได้รับผลกระทบยกเว้นขนาดคอนเทนเนอร์ใหม่มากกว่าความจุก่อนหน้า (ในกรณีนี้ตัววนซ้ำและการอ้างอิงทั้งหมดจะไม่ถูกต้อง) [23.2.4.3/1]
  • deque: ตัววนซ้ำและการอ้างอิงทั้งหมดจะไม่ถูกต้องยกเว้นว่าสมาชิกที่แทรกอยู่ที่จุดสิ้นสุด (ด้านหน้าหรือด้านหลัง) ของ deque (ในกรณีนี้ตัววนซ้ำทั้งหมดจะไม่ถูกต้อง แต่การอ้างอิงถึงองค์ประกอบจะไม่ได้รับผลกระทบ) [23.2.1.3/1]
  • list: ตัววนซ้ำและการอ้างอิงทั้งหมดไม่ได้รับผลกระทบ [23.2.2.3/1]

ภาชนะบรรจุที่เกี่ยวข้อง

  • [multi]{set,map}: ตัววนซ้ำและการอ้างอิงทั้งหมดไม่ได้รับผลกระทบ [23.1.2 / 8]

อะแดปเตอร์คอนเทนเนอร์

  • stack: สืบทอดมาจากคอนเทนเนอร์พื้นฐาน
  • queue: สืบทอดมาจากคอนเทนเนอร์พื้นฐาน
  • priority_queue: สืบทอดมาจากคอนเทนเนอร์พื้นฐาน

การลบออก

ภาชนะบรรจุลำดับ

  • vector: ตัววนซ้ำทั้งหมดและการอ้างอิงหลังจากจุดลบถูกยกเลิก [23.2.4.3/3]
  • deque: ตัววนซ้ำและการอ้างอิงทั้งหมดจะไม่ถูกต้องเว้นแต่ว่าสมาชิกที่ถูกลบจะอยู่ที่จุดสิ้นสุด (ด้านหน้าหรือด้านหลัง) ของ deque (ซึ่งในกรณีนี้เฉพาะตัววนซ้ำและการอ้างอิงถึงสมาชิกที่ถูกลบเท่านั้น) [23.2.1.3/4]
  • list: เฉพาะตัววนซ้ำและการอ้างอิงองค์ประกอบที่ถูกลบเท่านั้นจะไม่ถูกต้อง [23.2.2.3/3]

ภาชนะบรรจุที่เกี่ยวข้อง

  • [multi]{set,map}: เฉพาะตัววนซ้ำและการอ้างอิงองค์ประกอบที่ถูกลบเท่านั้นจะไม่ถูกต้อง [23.1.2 / 8]

อะแดปเตอร์คอนเทนเนอร์

  • stack: สืบทอดมาจากคอนเทนเนอร์พื้นฐาน
  • queue: สืบทอดมาจากคอนเทนเนอร์พื้นฐาน
  • priority_queue: สืบทอดมาจากคอนเทนเนอร์พื้นฐาน

ปรับขนาด

  • vector: ตามการแทรก / ลบ [23.2.4.2/6]
  • deque: ตามการแทรก / ลบ [23.2.1.2/1]
  • list: ตามการแทรก / ลบ [23.2.2.2/1]

หมายเหตุ 1

เว้นแต่จะระบุไว้เป็นอย่างอื่น (อย่างชัดเจนหรือโดยการกำหนดฟังก์ชั่นในแง่ของฟังก์ชั่นอื่น ๆ ) การเรียกใช้ฟังก์ชั่นสมาชิกตู้คอนเทนเนอร์หรือผ่านภาชนะที่เป็นข้อโต้แย้งไปยังฟังก์ชั่นห้องสมุดจะไม่ทำให้ iterators เป็นโมฆะหรือเปลี่ยนค่าของวัตถุภายในคอนเทนเนอร์นั้น . [23.1 / 11]

โน้ต 2

ไม่ชัดเจนใน C ++ 2003 ว่าตัววนซ้ำ "จบ" อยู่ภายใต้กฎข้างต้นหรือไม่ คุณควรสมมติว่าเป็นเช่นนั้น (เช่นนี้เป็นจริงในทางปฏิบัติ)

หมายเหตุ 3

กฎสำหรับการทำให้เป็นโมฆะของพอยน์เตอร์เป็น sames เป็นกฎสำหรับการทำให้เป็นโมฆะของการอ้างอิง


5
ความคิดที่ดีเพียงสังเกต: ฉันคิดว่าคอนเทนเนอร์แบบเชื่อมโยงสามารถพับเข้าด้วยกันในบรรทัดเดียวและมันอาจจะคุ้มค่าแล้วเพิ่มอีกหนึ่งบรรทัดของกลุ่มที่เชื่อมโยงที่ไม่ได้เรียงลำดับ ... แม้ว่าฉันจะไม่แน่ใจว่าส่วน rehashing นั้นจะเป็นอย่างไร แมปที่แทรก / ลบคุณรู้วิธีตรวจสอบว่ามีการเรียกใช้รูปแบบใหม่หรือไม่?
Matthieu M.

1
IIRC บางสเป็คบอกว่าจุดสิ้นสุดตัววนซ้ำไม่ใช่ตัววนซ้ำ "ไปยังวัตถุภายในคอนเทนเนอร์นั้น" ฉันสงสัยว่าการรับประกันเหล่านั้นมองหาตัววนซ้ำสุดท้ายในแต่ละกรณีอย่างไร
Johannes Schaub - litb

1
@MuhammadAnnaqeeb: คำตอบนี้เป็นที่ยอมรับไม่ได้ทำให้ชัดเจนเนื่องจากฉันใช้ทางลัด แต่ความตั้งใจที่จะกล่าวว่าการปรับขนาดเป็นการแทรก / ลบออกราวกับว่าจำเป็นต้องทำการจัดสรรใหม่คุณอาจคิดว่ามันเหมือนกับการลบ จากนั้นแทรกองค์ประกอบที่ได้รับผลกระทบทั้งหมดอีกครั้ง ส่วนของคำตอบนั้นสามารถปรับปรุงได้อย่างแน่นอน
Lightness Races ในวงโคจร

1
@Yakk: แต่มันไม่ได้; ดูข้อความมาตรฐานที่อ้างถึง ดูเหมือนว่าจะได้รับการแก้ไขใน C ++ 11 :)
Lightness Races ใน Orbit

1
@metamorphosis: deque เก็บข้อมูลในบล็อกที่ไม่ต่อเนื่องกัน การแทรกที่จุดเริ่มต้นหรือจุดสิ้นสุดอาจจัดสรรบล็อกใหม่ แต่จะไม่ย้ายไปรอบ ๆ องค์ประกอบก่อนหน้าดังนั้นตัวชี้ยังคงใช้ได้ แต่กฎสำหรับการไปยังองค์ประกอบถัดไป / ก่อนหน้านั้นเปลี่ยนแปลงหากมีการจัดสรรบล็อกใหม่ดังนั้นตัววนซ้ำจะไม่ถูกต้อง
Nick Matteo

357

C ++ 11 (ที่มา: กฎการยกเลิก Iterator (C ++ 0x) )


การแทรก

ภาชนะบรรจุลำดับ

  • vector: ตัววนซ้ำทั้งหมดและการอ้างอิงก่อนจุดแทรกจะไม่ได้รับผลกระทบยกเว้นขนาดคอนเทนเนอร์ใหม่มากกว่าความจุก่อนหน้า (ในกรณีนี้ตัววนซ้ำและการอ้างอิงทั้งหมดจะไม่ถูกต้อง) [23.3.6.5/1]
  • deque: ตัววนซ้ำและการอ้างอิงทั้งหมดจะไม่ถูกต้องยกเว้นว่าสมาชิกที่แทรกอยู่ที่จุดสิ้นสุด (ด้านหน้าหรือด้านหลัง) ของ deque (ในกรณีนี้ตัววนซ้ำทั้งหมดจะไม่ถูกต้อง แต่การอ้างอิงไปยังองค์ประกอบจะไม่ได้รับผลกระทบ) [23.3.3.4/1]
  • list: ตัววนซ้ำและการอ้างอิงทั้งหมดไม่ได้รับผลกระทบ [23.3.5.4/1]
  • forward_list: ตัววนซ้ำและการอ้างอิงทั้งหมดไม่ได้รับผลกระทบ(ใช้กับinsert_after) [23.3.4.5/1]
  • array: (n / a)

ภาชนะบรรจุที่เกี่ยวข้อง

  • [multi]{set,map}: ตัววนซ้ำและการอ้างอิงทั้งหมดไม่ได้รับผลกระทบ [23.2.4 / 9]

คอนเทนเนอร์ที่เชื่อมโยงไม่ได้เรียงกัน

  • unordered_[multi]{set,map}: ตัววนซ้ำทั้งหมดใช้งานไม่ได้เมื่อทำการ rehashing แต่การอ้างอิงไม่ได้รับผลกระทบ [23.2.5 / 8] การให้ความร้อนซ้ำจะไม่เกิดขึ้นหากการแทรกไม่ทำให้ขนาดของภาชนะเกินกว่าz * Bโดยที่zตัวประกอบภาระสูงสุดและBจำนวนที่เก็บปัจจุบัน [23.2.5 / 14]

อะแดปเตอร์คอนเทนเนอร์

  • stack: สืบทอดมาจากคอนเทนเนอร์พื้นฐาน
  • queue: สืบทอดมาจากคอนเทนเนอร์พื้นฐาน
  • priority_queue: สืบทอดมาจากคอนเทนเนอร์พื้นฐาน

การลบออก

ภาชนะบรรจุลำดับ

  • vector: ตัววนซ้ำทุกตัวและการอ้างอิงในหรือหลังจุดลบนั้นไม่ถูกต้อง [23.3.6.5/3]
  • deque: การลบองค์ประกอบสุดท้ายจะยกเลิกการทำซ้ำเท่านั้นและการอ้างอิงถึงองค์ประกอบที่ถูกลบและตัววนซ้ำในอดีต; การลบองค์ประกอบแรกจะทำให้การทำซ้ำเป็นโมฆะเท่านั้นและการอ้างอิงไปยังองค์ประกอบที่ถูกลบ การลบองค์ประกอบอื่น ๆ จะทำให้การทำซ้ำและการอ้างอิงทั้งหมดเป็นโมฆะ (รวมถึงตัววนซ้ำในอดีต) [23.3.3.4/4]
  • list: เฉพาะตัววนซ้ำและการอ้างอิงองค์ประกอบที่ถูกลบเท่านั้นจะไม่ถูกต้อง [23.3.5.4/3]
  • forward_list: เฉพาะตัววนซ้ำและการอ้างอิงถึงองค์ประกอบที่ถูกลบเท่านั้นจะไม่ถูกต้อง(ใช้กับerase_after) [23.3.4.5/1]
  • array: (n / a)

ภาชนะบรรจุที่เกี่ยวข้อง

  • [multi]{set,map}: เฉพาะตัววนซ้ำและการอ้างอิงองค์ประกอบที่ถูกลบเท่านั้นจะไม่ถูกต้อง [23.2.4 / 9]

คอนเทนเนอร์ที่เชื่อมโยงไม่ได้เรียงลำดับ

  • unordered_[multi]{set,map}: เฉพาะตัววนซ้ำและการอ้างอิงองค์ประกอบที่ถูกลบเท่านั้นจะไม่ถูกต้อง [23.2.5 / 13]

อะแดปเตอร์คอนเทนเนอร์

  • stack: สืบทอดมาจากคอนเทนเนอร์พื้นฐาน
  • queue: สืบทอดมาจากคอนเทนเนอร์พื้นฐาน
  • priority_queue: สืบทอดมาจากคอนเทนเนอร์พื้นฐาน

ปรับขนาด

  • vector: ตามการแทรก / ลบ [23.3.6.5/12]
  • deque: ตามการแทรก / ลบ [23.3.3.3/3]
  • list: ตามการแทรก / ลบ [23.3.5.3/1]
  • forward_list: ตามการแทรก / ลบ [23.3.4.5/25]
  • array: (n / a)

หมายเหตุ 1

เว้นแต่จะระบุไว้เป็นอย่างอื่น (อย่างชัดเจนหรือโดยการกำหนดฟังก์ชั่นในแง่ของฟังก์ชั่นอื่น ๆ ) การเรียกใช้ฟังก์ชั่นสมาชิกตู้คอนเทนเนอร์หรือผ่านภาชนะที่เป็นข้อโต้แย้งไปยังฟังก์ชั่นห้องสมุดจะไม่ทำให้ iterators เป็นโมฆะหรือเปลี่ยนค่าของวัตถุภายในคอนเทนเนอร์นั้น . [23.2.1 / 11]

โน้ต 2

ไม่มีฟังก์ชั่น swap () ทำให้การอ้างอิงพอยน์เตอร์หรือตัววนซ้ำ อ้างอิงกับองค์ประกอบของคอนเทนเนอร์ที่เปลี่ยน [หมายเหตุ: ตัวสิ้นสุด () ตัววนซ้ำไม่ได้อ้างอิงองค์ประกอบใด ๆ ดังนั้นจึงอาจใช้การไม่ได้ - บันทึกท้าย] [23.2.1 / 10]

หมายเหตุ 3

นอกเหนือจากข้างต้นเกี่ยวกับข้อแม้swap(), มันไม่ชัดเจนว่า "จบ" iterators อาจมีการระบุไว้ข้างต้นกฎต่อภาชนะ ; คุณควรสมมติว่าพวกเขาเป็น

หมายเหตุ 4

vectorและภาชนะบรรจุที่เชื่อมโยงเรียงลำดับสนับสนุนซึ่งรับประกันได้ว่าไม่มีการปรับขนาดโดยอัตโนมัติจะเกิดขึ้นอย่างน้อยจนถึงขนาดของภาชนะเติบโตreserve(n) nข้อควรระวังจะต้องดำเนินการกับภาชนะบรรจุที่เชื่อมโยงเรียงลำดับเพราะข้อเสนอในอนาคตจะช่วยให้คุณสมบัติของปัจจัยโหลดขั้นต่ำซึ่งจะช่วยให้ rehashing ที่จะเกิดขึ้นบนinsertหลังพอeraseการดำเนินงานลดขนาดภาชนะต่ำกว่าขั้นต่ำนั้น eraseรับประกันควรพิจารณาที่อาจเกิดขึ้นหลังจากที่เป็นโมฆะ


นอกจากswap()นี้กฎสำหรับความถูกต้องของตัววนซ้ำเมื่อคัดลอก / ย้ายกำหนดคืออะไร
goodbyeera

@LightnessRacesinOrbit: เช่นเดียวกับการแทรกการลบการปรับขนาดและการสลับคัดลอก / ย้ายที่ได้รับมอบหมายยังเป็นฟังก์ชั่นสมาชิกของ std :: vector ดังนั้นฉันคิดว่าคุณสามารถให้กฎความถูกต้องของตัววนซ้ำสำหรับพวกเขาได้เช่นกัน
goodbyeera

@goodbyeera: คุณหมายถึงการคัดลอก / ย้ายการกำหนดองค์ประกอบหรือไม่ สิ่งนี้จะไม่ส่งผลกระทบต่อตัววนซ้ำใด ๆ ทำไมมันจะ? คุณกำลังกดหมายเหตุ 1ด้านบน
การแข่งขัน Lightness ใน Orbit

1
ฉันคิดว่าฉันทำผิดเพราะstd::basic_stringดูเหมือนจะไม่ถูกนับว่าเป็นตู้คอนเทนเนอร์และแน่นอนว่าไม่ใช่ตู้คอนเทนเนอร์ในส่วนของมาตรฐานที่บันทึกใช้ ยังไม่บอกว่า SSO ไม่ได้รับอนุญาตที่ไหน (ฉันรู้ว่า COW คืออะไร)
Deduplicator

2
กฎเหล่านี้เหมือนกันทั้งหมดใน C ++ 14 หรือไม่ C ++ 17 (เท่าที่ทราบกันแล้ว)?
einpoklum

40

มันอาจจะเป็นมูลค่าเพิ่มที่ iterator แทรกทุกชนิด ( std::back_insert_iterator, std::front_insert_iterator, std::insert_iterator) รับประกันได้ว่าจะยังคงใช้ได้ตราบเท่าที่แทรกทั้งหมดจะดำเนินการผ่าน iterator นี้และไม่มีเหตุการณ์อื่น iterator-ต้นเหตุอิสระเกิดขึ้น

ตัวอย่างเช่นเมื่อคุณดำเนินการชุดของการแทรกลงใน a std::vectorโดยใช้std::insert_iteratorมันเป็นไปได้มากที่การแทรกเหล่านี้จะทำให้การจัดสรรคืนเวกเตอร์ซึ่งจะทำให้การวนซ้ำทั้งหมดที่ "ชี้" เป็นเวกเตอร์นั้นไม่ถูกต้อง อย่างไรก็ตามการแทรกตัววนซ้ำในคำถามรับประกันว่าจะยังคงใช้ได้เช่นคุณสามารถดำเนินการลำดับการแทรกได้อย่างปลอดภัย ไม่จำเป็นต้องกังวลเกี่ยวกับการเรียกคืนค่าการจัดสรรเวกเตอร์เลย

สิ่งนี้จะใช้กับการแทรกที่ดำเนินการผ่านตัวแทรกตัววนซ้ำเท่านั้น หากเหตุการณ์ที่ทำให้การวนซ้ำไม่ถูกต้องถูกเรียกใช้โดยการกระทำบางอย่างที่เป็นอิสระบนคอนเทนเนอร์ดังนั้นตัววนซ้ำการแทรกจะกลายเป็นโมฆะและสอดคล้องกับกฎทั่วไป

ตัวอย่างเช่นรหัสนี้

std::vector<int> v(10);
std::vector<int>::iterator it = v.begin() + 5;
std::insert_iterator<std::vector<int> > it_ins(v, it);

for (unsigned n = 20; n > 0; --n)
  *it_ins++ = rand();

มีการรับประกันว่าจะดำเนินการตามลำดับที่ถูกต้องของการแทรกลงในเวกเตอร์แม้ว่าเวกเตอร์ "ตัดสินใจ" ที่จะจัดสรรใหม่บางแห่งในช่วงกลางของกระบวนการนี้ itเห็นได้ชัดว่าIterator จะใช้งานไม่ได้ แต่it_insจะยังคงใช้ได้ต่อไป


22

เนื่องจากคำถามนี้ได้รับการโหวตมากมายและกลายเป็นคำถามที่พบบ่อยฉันจึงควรเขียนคำตอบแยกต่างหากเพื่อพูดถึงความแตกต่างที่สำคัญระหว่าง C ++ 03 และ C ++ 11 เกี่ยวกับผลกระทบของการstd::vectorแทรกของ ความถูกต้องของตัววนซ้ำและการอ้างอิงที่เกี่ยวข้องกับreserve()และcapacity()ซึ่งคำตอบที่ upvoted ที่สุดไม่สามารถสังเกตเห็นได้

C ++ 03:

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

C ++ 11:

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

ดังนั้นใน C ++ 03 ไม่ใช่ " unless the new container size is greater than the previous capacity (in which case all iterators and references are invalidated)" ดังที่กล่าวไว้ในคำตอบอื่น ๆ แต่ควรเป็น " greater than the size specified in the most recent call to reserve()" นี่คือสิ่งหนึ่งที่ C ++ 03 แตกต่างจาก C ++ 11 ใน C ++ 03 เมื่อinsert()ขนาดของเวกเตอร์ถึงค่าที่ระบุในการreserve()โทรก่อนหน้านี้(ซึ่งอาจมีขนาดเล็กกว่าปัจจุบันcapacity()เนื่องจาก a reserve()อาจส่งผลให้มีขนาดใหญ่capacity()กว่าที่ขอ) การเปลี่ยนแปลงใด ๆ ที่ตามมาinsert()อาจทำให้การจัดสรรใหม่และทำให้ใช้งานไม่ได้ ตัววนซ้ำและการอ้างอิงทั้งหมด ใน C ++ 11 สิ่งนี้จะไม่เกิดขึ้นและคุณสามารถไว้วางใจcapacity()ให้รู้ได้อย่างแน่นอนว่าการจัดสรรครั้งต่อไปจะไม่เกิดขึ้นก่อนที่ขนาดจะcapacity()เกิน

โดยสรุปหากคุณทำงานกับเวกเตอร์ C ++ 03 และคุณต้องการให้แน่ใจว่าการจัดสรรใหม่จะไม่เกิดขึ้นเมื่อคุณทำการแทรกค่าของอาร์กิวเมนต์ที่คุณส่งไปก่อนหน้านี้reserve()ควรตรวจสอบขนาดไม่ใช่ ค่าตอบแทนของการโทรไปหาcapacity()มิฉะนั้นคุณอาจทำให้คุณประหลาดใจที่การจัดสรรใหม่ " ก่อนกำหนด "


14
อย่างไรก็ตามฉันจะยิงผู้รวบรวมผู้ใดทำสิ่งนี้กับฉันและไม่มีคณะลูกขุนในแผ่นดินที่จะลงโทษฉัน
Yakk - Adam Nevraumont

9
ฉันไม่ได้ "ไม่สังเกต" สิ่งนี้; มันเป็นข้อผิดพลาดทางบรรณาธิการใน C ++ 03 ที่ได้รับการแก้ไขใน C ++ 11 ไม่มีคอมไพเลอร์หลักใช้ประโยชน์จากข้อผิดพลาด
การแข่งขัน Lightness ใน Orbit

1
@Yakk ฉันคิดว่า gcc ทำให้การทำซ้ำในสถานการณ์เช่นนี้เป็นโมฆะ
ShreevatsaR

2

นี่คือตารางสรุปที่ดีจากcppreference.com :

ป้อนคำอธิบายรูปภาพที่นี่

ที่นี่การแทรกหมายถึงวิธีการใด ๆ ที่เพิ่มหนึ่งหรือมากกว่าหนึ่งองค์ประกอบไปยังภาชนะและการลบหมายถึงวิธีการใด ๆ ที่เอาหนึ่งหรือมากกว่าหนึ่งองค์ประกอบจากภาชนะ

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