basic_string
ห้ามวัวใน C ++ 11 ขึ้นไปหรือไม่?
เกี่ยวกับ
”ฉันถูกต้องที่ C ++ 11 ไม่ยอมรับการใช้งานตามวัวของstd::string
?
ใช่.
เกี่ยวกับ
”ถ้าเป็นเช่นนั้นข้อ จำกัด นี้มีการระบุไว้อย่างชัดเจนในมาตรฐานใหม่ (ที่ไหน) หรือไม่?
เกือบโดยตรงตามข้อกำหนดของความซับซ้อนคงที่สำหรับการดำเนินการหลายอย่างที่ต้องการ O ( n ) การคัดลอกข้อมูลสตริงทางกายภาพในการใช้งาน COW
ตัวอย่างเช่นสำหรับฟังก์ชันสมาชิก
auto operator[](size_type pos) const -> const_reference;
auto operator[](size_type pos) -> reference;
…ซึ่งในการใช้งาน COW จะทริกเกอร์การคัดลอกข้อมูลสตริงทั้งสองเพื่อยกเลิกการแชร์ค่าสตริงมาตรฐาน C ++ 11 ต้องการ
C ++ 11 §21.4.5 / 4 :
" ความซับซ้อน:เวลาคงที่
…ซึ่งออกกฎการคัดลอกข้อมูลดังกล่าวและด้วยเหตุนี้ COW
C ++ 03 ได้รับการสนับสนุนการใช้งาน COW โดยไม่ได้มีความต้องการที่ซับซ้อนอย่างต่อเนื่องเหล่านี้และโดยภายใต้เงื่อนไขที่เข้มงวดบางอย่างที่ช่วยให้โทรไปoperator[]()
, at()
, begin()
, rbegin()
, end()
หรือrend()
อ้างอิงโมฆะตัวชี้และ iterators หมายถึงรายการสตริงเช่นจะอาจเกิดขึ้น การคัดลอกข้อมูล COW การสนับสนุนนี้ถูกลบออกใน C ++ 11
ห้ามวัวผ่านกฎการยกเลิก C ++ 11 ด้วยหรือไม่
ในคำตอบอื่นซึ่งในขณะที่เขียนถูกเลือกให้เป็นวิธีแก้ปัญหาและซึ่งได้รับการโหวตอย่างมากและเห็นได้ชัดว่ามีการยืนยันว่า
"สำหรับสตริง COW การเรียกแบบไม่const
operator[]
ต้องทำสำเนา (และการอ้างอิงที่ไม่ถูกต้อง) ซึ่งไม่อนุญาตโดยย่อหน้า [ที่ยกมา] ด้านบน [C ++ 11 §21.4.1 / 6] ดังนั้นจึงไม่ถูกกฎหมายอีกต่อไปที่จะมีสตริง COW ใน C ++ 11
การยืนยันนั้นไม่ถูกต้องและทำให้เข้าใจผิดในสองวิธีหลัก:
- มันบ่งชี้อย่างไม่ถูกต้องว่าเฉพาะผู้เข้าถึงที่ไม่ใช่
const
รายการเท่านั้นที่ต้องทริกเกอร์การคัดลอกข้อมูล COW
แต่ผู้เข้าถึงconst
รายการจำเป็นต้องทริกเกอร์การคัดลอกข้อมูลเนื่องจากอนุญาตให้รหัสไคลเอ็นต์สร้างการอ้างอิงหรือตัวชี้ที่ (ใน C ++ 11) ไม่อนุญาตให้ยกเลิกการใช้งานในภายหลังผ่านการดำเนินการที่สามารถทริกเกอร์การคัดลอกข้อมูล COW
- สันนิษฐานอย่างไม่ถูกต้องว่าการคัดลอกข้อมูล COW อาจทำให้การอ้างอิงไม่ถูกต้อง
แต่ในการนำไปใช้งานที่ถูกต้องการคัดลอกข้อมูล COW การยกเลิกการแชร์ค่าสตริงจะกระทำ ณ จุดหนึ่งก่อนที่จะมีการอ้างอิงใด ๆ ที่อาจทำให้ไม่ถูกต้องได้
หากต้องการดูว่าการใช้งาน C ++ 11 COW ที่ถูกต้องbasic_string
จะทำงานอย่างไรเมื่อข้อกำหนด O (1) ที่ทำให้ไม่ถูกต้องถูกละเว้นให้นึกถึงการใช้งานที่สตริงสามารถสลับระหว่างนโยบายการเป็นเจ้าของได้ อินสแตนซ์สตริงเริ่มต้นด้วย Policy Sharable เมื่อใช้นโยบายนี้แล้วจะไม่มีการอ้างอิงรายการภายนอก อินสแตนซ์สามารถเปลี่ยนไปใช้นโยบายที่ไม่ซ้ำกันได้และจะต้องดำเนินการดังกล่าวเมื่อมีการสร้างการอ้างอิงรายการเช่นด้วยการเรียกไปที่.c_str()
(อย่างน้อยหากสร้างตัวชี้ไปยังบัฟเฟอร์ภายใน) ในกรณีทั่วไปของอินสแตนซ์หลายอินสแตนซ์ร่วมกันเป็นเจ้าของค่าสิ่งนี้เกี่ยวข้องกับการคัดลอกข้อมูลสตริง หลังจากนั้นการเปลี่ยนไปใช้นโยบายที่ไม่ซ้ำกันอินสแตนซ์จะเปลี่ยนกลับไปเป็นแชร์ได้โดยการดำเนินการที่ทำให้การอ้างอิงทั้งหมดเป็นโมฆะเช่นการมอบหมาย
ดังนั้นในขณะที่ข้อสรุปของคำตอบนั้นสตริง COW ถูกตัดออกนั้นถูกต้อง แต่เหตุผลที่เสนอนั้นไม่ถูกต้องและทำให้เข้าใจผิดอย่างมาก
ฉันสงสัยว่าสาเหตุของความเข้าใจผิดนี้เป็นบันทึกที่ไม่เป็นบรรทัดฐานในภาคผนวก C ของ C ++ 11:
C ++ 11 §C.2.11 [diff.cpp03.strings] ประมาณ§21.3:
การเปลี่ยนแปลง : basic_string
ข้อกำหนดไม่อนุญาตให้ใช้สตริงที่นับการอ้างอิงอีกต่อไป
เหตุผล:การไม่ถูกต้องแตกต่างกันเล็กน้อยกับสตริงที่นับการอ้างอิง การเปลี่ยนแปลงนี้ทำให้พฤติกรรม (sic) เป็นปกติสำหรับมาตรฐานสากลนี้
ผลกระทบต่อคุณสมบัติดั้งเดิม:โค้ด C ++ 2003 ที่ถูกต้องอาจทำงานแตกต่างกันในมาตรฐานสากลนี้
นี่คือเหตุผลที่อธิบายถึงสาเหตุหลักว่าเหตุใดจึงตัดสินใจลบการสนับสนุนพิเศษ C ++ 03 COW เหตุผลนี้เหตุผลที่ไม่ได้เป็นวิธีมาตรฐานได้อย่างมีประสิทธิภาพไม่อนุญาตให้ดำเนินการ COW มาตรฐานไม่อนุญาต COW ผ่านข้อกำหนด O (1)
ในระยะสั้น, C ++ 11 std::basic_string
กฎโมฆะไม่ออกกฎการดำเนินงานของวัว แต่พวกเขาแยกแยะการใช้งาน COW สไตล์ C ++ 03 ที่มีประสิทธิภาพอย่างสมเหตุสมผลเช่นเดียวกับการใช้งานไลบรารีมาตรฐานของ g ++ อย่างน้อยหนึ่งอย่าง การสนับสนุน C ++ 03 COW แบบพิเศษช่วยให้สามารถใช้งานได้อย่างมีประสิทธิภาพโดยเฉพาะอย่างยิ่งการใช้ตัวเข้าถึงconst
รายการโดยมีค่าใช้จ่ายของกฎที่ละเอียดอ่อนและซับซ้อนสำหรับการยกเลิกการใช้งาน:
C ++ 03 §21.3 / 5ซึ่งรวมถึงการสนับสนุน COW "การโทรครั้งแรก":
"การอ้างอิงตัวชี้และตัวทำซ้ำที่อ้างถึงองค์ประกอบของbasic_string
ลำดับอาจไม่ถูกต้องโดยการใช้งานต่อไปนี้ของbasic_string
วัตถุนั้น:
- เป็นอาร์กิวเมนต์สำหรับฟังก์ชันที่ไม่ใช่สมาชิกswap()
(21.3.7.8), operator>>()
(21.3.7.9) และgetline()
(21.3. 7.9)
- เป็นข้อโต้แย้งbasic_string::swap()
ของ
- ฟังก์ชั่นการโทรdata()
และc_str()
สมาชิก
- โทรที่ไม่ใช่const
สมาชิกฟังก์ชันยกเว้นoperator[]()
, at()
, begin()
, rbegin()
, และend()
- ภายหลังการใด ๆ ของการใช้งานดังกล่าวข้างต้นยกเว้นรูปแบบของและที่กลับ iterators, สายแรกที่ไม่ใช่สมาชิกฟังก์ชัน, , , ,rend()
insert()
erase()
const
operator[]()
at()
begin()
rbegin()
end()
หรือrend()
.
กฎเหล่านี้ซับซ้อนและละเอียดอ่อนมากจนฉันสงสัยว่าโปรแกรมเมอร์หลายคนสามารถสรุปได้อย่างแม่นยำ ฉันไม่สามารถ.
จะเกิดอะไรขึ้นถ้าข้อกำหนด O (1) ถูกเพิกเฉย
หากoperator[]
ไม่คำนึงถึงข้อกำหนดเวลาคงที่ของ C ++ 11 ในเช่นนั้น COW สำหรับbasic_string
อาจเป็นไปได้ในทางเทคนิค แต่ยากที่จะนำไปใช้
การดำเนินการที่สามารถเข้าถึงเนื้อหาของสตริงโดยไม่ต้องคัดลอกข้อมูล COW ได้แก่ :
- การเชื่อมต่อผ่าน
+
.
- เอาต์พุตผ่าน
<<
.
- การใช้
basic_string
อาร์กิวเมนต์เป็นฟังก์ชันไลบรารีมาตรฐาน
เนื่องจากไลบรารีมาตรฐานได้รับอนุญาตให้อาศัยความรู้และโครงสร้างเฉพาะในการนำไปใช้งาน
นอกจากนี้การใช้งานสามารถนำเสนอฟังก์ชันที่ไม่ได้มาตรฐานต่างๆสำหรับการเข้าถึงเนื้อหาสตริงโดยไม่ต้องเรียกใช้การคัดลอกข้อมูล COW
ปัจจัยที่ทำให้เกิดความซับซ้อนหลักคือในbasic_string
การเข้าถึงรายการC ++ 11 ต้องทริกเกอร์การคัดลอกข้อมูล (ยกเลิกการแบ่งปันข้อมูลสตริง) แต่จำเป็นต้องไม่โยนเช่น C ++ 11 §21.4.5 / 3“ Throws: Nothing” ดังนั้นจึงไม่สามารถใช้การจัดสรรแบบไดนามิกธรรมดาเพื่อสร้างบัฟเฟอร์ใหม่สำหรับการคัดลอกข้อมูล COW วิธีหนึ่งในการแก้ปัญหานี้คือการใช้ฮีปพิเศษที่สามารถจองหน่วยความจำได้โดยไม่ต้องจัดสรรจริงจากนั้นจองจำนวนที่จำเป็นสำหรับการอ้างอิงเชิงตรรกะแต่ละค่าเป็นสตริง การจองและยกเลิกการจองในฮีปอาจเป็นเวลาคงที่ O (1) และการจัดสรรจำนวนเงินที่จองไว้แล้วสามารถnoexcept
. เพื่อให้เป็นไปตามข้อกำหนดของมาตรฐานด้วยวิธีนี้ดูเหมือนว่าจะต้องมีฮีปตามการจองพิเศษต่อผู้จัดสรรที่แตกต่างกัน
หมายเหตุ:
¹ตัวเข้าถึงconst
รายการทริกเกอร์การคัดลอกข้อมูล COW เนื่องจากอนุญาตให้โค้ดไคลเอ็นต์รับข้อมูลอ้างอิงหรือตัวชี้ไปยังข้อมูลซึ่งไม่ได้รับอนุญาตให้ทำให้เป็นโมฆะโดยการคัดลอกข้อมูลในภายหลังที่ทริกเกอร์โดยเช่นตัวเข้าถึงที่ไม่ใช่const
รายการ