เหตุใดเวกเตอร์ <bool> ของ libc ++ :: const_reference จึงไม่ใช่บูล


92

มาตรา 23.3.7 คลาสvector<bool>[vector.bool] วรรค 1 ระบุ:

template <class Allocator> class vector<bool, Allocator> {
public:
    // types:
    typedef bool              const_reference;
    ...

อย่างไรก็ตามโปรแกรมนี้ไม่สามารถคอมไพล์เมื่อใช้ libc ++:

#include <vector>
#include <type_traits>

int
main()
{
    static_assert(std::is_same<std::vector<bool>::const_reference, bool>{}, "?");
}

นอกจากนี้ฉันทราบว่ามาตรฐาน C ++ มีความสอดคล้องกันในข้อกำหนดนี้ตลอดไปจนถึง C ++ 98 และฉันทราบเพิ่มเติมว่า libc ++ ไม่ได้ปฏิบัติตามข้อกำหนดนี้อย่างต่อเนื่องตั้งแต่การเปิดตัว libc ++ ครั้งแรก

อะไรคือแรงจูงใจสำหรับการไม่ปฏิบัติตามนี้?

คำตอบ:


99

แรงจูงใจสำหรับส่วนขยายนี้ซึ่งตรวจพบได้โดยโปรแกรมที่สอดคล้องและไม่เป็นไปตามนั้นคือการทำให้vector<bool>พฤติกรรมเหมือนมากขึ้นvector<char>เมื่อเทียบกับการอ้างอิง (const และอื่น ๆ )

บทนำ

ตั้งแต่ปี 1998 vector<bool>ถูกเยาะเย้ยว่า "ไม่ใช่ตู้คอนเทนเนอร์" LWG 96ซึ่งเป็นหนึ่งในปัญหา LWG แรกเริ่มการอภิปราย วันนี้ 17 ปีต่อมาvector<bool>ส่วนใหญ่ยังคงไม่เปลี่ยนแปลง

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

สรุป : vector<bool>ไม่ใช่คอนเทนเนอร์ที่ไม่ดี มันมีประโยชน์มาก มันมีแค่ชื่อเสีย

กลับไปยัง const_reference

ตามที่แนะนำไว้ข้างต้นและมีรายละเอียดที่นี่สิ่งที่ไม่ดีเกี่ยวกับการvector<bool>ทำงานในโค้ดทั่วไปแตกต่างจากvectorอินสแตนซ์อื่น ๆ นี่คือตัวอย่างที่เป็นรูปธรรม:

#include <cassert>
#include <vector>

template <class T>
void
test(std::vector<T>& v)
{
    using const_ref = typename std::vector<T>::const_reference;
    const std::vector<T>& cv = v;
    const_ref cr = cv[0];
    assert(cr == cv[0]);
    v[0] = 1;
    assert(true == cv[0]);
    assert(cr == cv[0]);  // Fires!
}

int
main()
{
    std::vector<char> vc(1);
    test(vc);
    std::vector<bool> vb(1);
    test(vb);
}

ข้อกำหนดมาตรฐานระบุว่าการยืนยันที่ทำเครื่องหมาย// Fires!จะทริกเกอร์ แต่เมื่อtestรันด้วยไฟล์vector<bool>. เมื่อรันด้วยvector<char>(หรือvectorอื่นๆboolเมื่อมีการกำหนดค่าที่ไม่ใช่ค่าเริ่มต้นที่เหมาะสมT) การทดสอบจะผ่านไป

การใช้ libc ++ พยายามลดผลกระทบเชิงลบของการvector<bool>ทำงานที่แตกต่างกันในโค้ดทั่วไป สิ่งหนึ่งที่ทำได้เพื่อให้บรรลุเป้าหมายนี้คือการสร้าง vector<T>::const_referenceการอ้างอิงพร็อกซีเช่นเดียวกับที่ระบุvector<T>::referenceยกเว้นว่าคุณไม่สามารถกำหนดผ่านมันได้ นั่นคือบน libc ++ โดยvector<T>::const_referenceพื้นฐานแล้วจะเป็นตัวชี้ไปที่บิตภายในตัวvectorแทนที่จะเป็นสำเนาของบิตนั้น

บน libc ++ ข้างต้นtestส่งผ่านสำหรับทั้งสองvector<char>และvector<bool>.

ราคาเท่าไหร่?

ข้อเสียคือส่วนขยายนี้ตรวจพบดังที่แสดงในคำถาม อย่างไรก็ตามมีโปรแกรมเพียงไม่กี่โปรแกรมเท่านั้นที่สนใจเกี่ยวกับประเภทของนามแฝงนี้และโปรแกรมอื่น ๆ ก็ให้ความสำคัญกับพฤติกรรม

อะไรคือแรงจูงใจสำหรับการไม่ปฏิบัติตามนี้?

เพื่อให้ไคลเอ็นต์ libc ++ มีพฤติกรรมที่ดีขึ้นในโค้ดทั่วไปและบางทีหลังจากการทดสอบภาคสนามเพียงพอแล้วให้เสนอส่วนขยายนี้เป็นมาตรฐาน C ++ ในอนาคตเพื่อการปรับปรุงอุตสาหกรรม C ++ ทั้งหมด

ข้อเสนอดังกล่าวอาจมาในรูปแบบของคอนเทนเนอร์ใหม่ (เช่นbit_vector) ที่มี API แบบเดียวกับปัจจุบันvector<bool>แต่มีการอัปเกรดเล็กน้อยเช่นที่const_referenceกล่าวถึงในที่นี้ ตามด้วยการเลิกใช้งาน (และการนำออกในที่สุด) ของvector<bool>ความเชี่ยวชาญพิเศษ bitsetยังสามารถใช้การอัปเกรดเล็กน้อยในแผนกนี้เช่นเพิ่มconst_referenceและชุดตัวทำซ้ำ

นั่นคือในการหวนbitsetคือการvector<bool>(ซึ่งควรจะเปลี่ยนชื่อไปbit_vector- หรืออะไรก็ตาม) เป็นคือการarray vectorและการเปรียบเทียบควรจะถือเป็นจริงหรือไม่ที่เรากำลังพูดถึงboolเป็นvalue_typeของและvectorarray

มีหลายตัวอย่างของคุณลักษณะ C ++ 11 และ C ++ 14 ที่เริ่มต้นเป็นส่วนขยายใน libc ++ นี่คือการพัฒนามาตรฐาน ประสบการณ์ด้านบวกที่แสดงให้เห็น จริงมีอิทธิพลอย่างมาก พื้นบ้านมาตรฐานเป็นกลุ่มที่อนุรักษ์นิยมเมื่อต้องเปลี่ยนข้อกำหนดที่มีอยู่ (ตามที่ควรจะเป็น) การเดาแม้ว่าคุณจะแน่ใจว่าคุณเดาถูก แต่ก็เป็นกลยุทธ์ที่มีความเสี่ยงในการพัฒนามาตรฐานที่เป็นที่ยอมรับในระดับสากล


1
คำถาม: สามารถ / ร่างข้อเสนอล่าสุดเกี่ยวกับตัวทำซ้ำพร็อกซีโดย @EricNiebler ทำให้ส่วนขยาย libc ++ ถูกต้องตามกฎหมายและวางvector<bool>รากฐานชั้นหนึ่งได้หรือไม่?
TemplateRex

หมายเหตุ: ฉันต้องการให้ a vector_bool<Alloc>และarray_bool<N>เพื่อแสดงเวอร์ชันที่บรรจุ (รวมถึงตัววนซ้ำพร็อกซีการเข้าถึงโดยสุ่มที่วนซ้ำบิตทั้งหมด) ของvector<bool, Alloc>และarray<bool, N>. อย่างไรก็ตามbitset<N>(และเป็นลูกพี่ลูกน้องboost::dynamic_bitset<Alloc>) เป็นตัวแทนของนามธรรมที่แตกต่างกันนั่นคือเวอร์ชันบรรจุของstd::set<int>. ดังนั้นฉันต้องการมีพูดbit_array<N>และbit_vector<Alloc>เป็นผู้สืบทอดแฟรนไชส์บิตเซ็ตโดยมีตัวทำซ้ำแบบสองทิศทางที่เหมาะสม(วนซ้ำใน 1 บิตแทนที่จะเป็นบิตทั้งหมด) คุณคิดอย่างไรกับเรื่องนี้
TemplateRex

5
ข้อเสนอแบบร่างของฉันเกี่ยวกับตัวทำซ้ำพร็อกซีจะสร้างvector<bool>คอนเทนเนอร์การเข้าถึงแบบสุ่มที่สอดคล้องกับมาตรฐาน มันจะไม่ทำให้vector<bool>เป็นความคิดที่ดี :-) ฉันเห็นด้วยกับ Howard มันควรจะเรียกว่าอย่างอื่น
Eric Niebler

1
เหตุใดจึงไม่มีวิธีเลือกไม่ใช้ส่วนขยาย libc ++ และปฏิบัติตามพฤติกรรมอย่างเคร่งครัด (ฉันไม่ได้ขอให้ทำให้สอดคล้องกับค่าเริ่มต้นเพียงวิธีปิดการใช้งานส่วนขยายของ libc ++ เพื่อให้สามารถเขียนโค้ดพกพาได้) อย่างที่คุณรู้ว่าฉันถูกกัดโดยส่วนขยาย tuple libc ++ ในอดีตและเมื่อเร็ว ๆ นี้ถูกกัดโดยส่วนขยาย bitset :: const_reference
gnzlbg

5
@gnzlbg: มีทรัพยากรทางเศรษฐกิจและทางโลกจำนวน จำกัด สำหรับการพัฒนา libc ++ ครั้งแรก ต่อจากนั้นการใช้งานก็ถึงวาระที่จะไม่ทำให้ผู้ใช้ทุกคนมีความสุข ด้วยทรัพยากรที่มีอยู่การแลกเปลี่ยนทางวิศวกรรมถูกสร้างขึ้นเพื่อพยายามเพิ่มจำนวนผู้ใช้ที่มีความสุขให้มากที่สุดเพิ่มประโยชน์สูงสุดให้กับชุมชน C ++ โดยรวม ขออภัยเกี่ยวกับประสบการณ์ของคุณ ฉันทราบว่าส่วนขยายทูเพิลที่คุณใช้งานอยู่ตอนนี้อยู่ในเอกสารการทำงาน C ++ 1z ปัจจุบัน ในประเด็นนั้นคุณได้เสียสละโดยไม่รู้ตัวเพื่อให้หลาย ๆ คนได้รับประโยชน์และคนจำนวนมากเหล่านั้นก็เป็นหนี้บุญคุณ
Howard Hinnant
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.