ความแตกต่างระหว่าง const_iterator และ non-const iterator ใน C ++ STL คืออะไร?


146

อะไรคือความแตกต่างระหว่าง a const_iteratorและ an iteratorและคุณจะใช้อีกอันหนึ่งตรงไหน


2
ชื่อ const_iterator ดูเหมือนตัววนซ้ำคือ const ในขณะที่ตัววนซ้ำนี้ชี้ไปคือ const จริง
talekeDskobeDa

คำตอบ:


131

const_iterators ไม่อนุญาตให้คุณเปลี่ยนค่าที่พวกเขาชี้ไปปกติiterators ทำ

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


9
ในโลกที่สมบูรณ์แบบที่จะเป็นเช่นนั้น แต่ด้วย C ++ const นั้นดีเท่ากับคนที่เขียนโค้ดเท่านั้น :(
JaredPar

ไม่แน่นอนมีอยู่ด้วยเหตุผลที่ดีมาก ไม่ค่อยมีการใช้งานและเมื่อดูที่ Google Code Search ดูเหมือนว่าจะมีการใช้งานที่ถูกต้องเป็นเปอร์เซ็นต์ คำหลักเป็นเครื่องมือเพิ่มประสิทธิภาพที่มีประสิทธิภาพมากและก็ไม่ชอบถอดมันออกจะปรับปรุง const-ความถูกต้อง ( coughpointerscoughandcoughreferencescough )
coppro

2
เหมือนแฮ็คที่ทรงพลัง ในทุกกรณีที่ฉันเคยเห็นเกี่ยวกับการใช้คำหลัก 'เปลี่ยนแปลงได้' ทั้งหมดนี้เป็นตัวบ่งชี้ที่ถูกต้องว่ารหัสนั้นเขียนไม่ดีและไม่สามารถเปลี่ยนแปลงได้เป็นสิ่งจำเป็นในการแฮ็คเพื่อแก้ไขข้อบกพร่อง
John Dibling

7
มีการใช้งานที่ถูกต้องเช่นการแคชผลลัพธ์ของการคำนวณแบบยาวภายในคลาส const ในทางกลับกันนั่นเป็นครั้งเดียวที่ฉันใช้การเปลี่ยนแปลงที่ไม่แน่นอนในช่วงเกือบยี่สิบปีของการพัฒนา C ++
Head Geek

ตัวอย่างทั่วไปสำหรับการใช้ const_iterator คือเมื่อตัววนซ้ำเป็น rvalue
talekeDskobeDa

41

พวกเขาควรจะอธิบายตัวเองได้ดีทีเดียว หากตัววนรอบชี้ไปที่องค์ประกอบประเภท T ดังนั้น const_iterator จะชี้ไปที่องค์ประกอบประเภท 'const T'

โดยพื้นฐานแล้วมันเทียบเท่ากับประเภทตัวชี้:

T* // A non-const iterator to a non-const element. Corresponds to std::vector<T>::iterator
T* const // A const iterator to a non-const element. Corresponds to const std::vector<T>::iterator
const T* // A non-const iterator to a const element. Corresponds to std::vector<T>::const_iterator

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


1
"const iterator ชี้ไปที่องค์ประกอบเดียวกันเสมอ" ซึ่งไม่ถูกต้อง
John Dibling

2
ยังไง? สังเกตขีดล่างที่ขาดหายไป ฉันกำลังตัดกันตัวแปรประเภท const std :: vector <T> :: iterator กับ std :: vector <T> :: const_iterator ในกรณีเดิมตัววนซ้ำเองคือ const ดังนั้นจึงไม่สามารถแก้ไขได้ แต่องค์ประกอบที่อ้างอิงสามารถแก้ไขได้อย่างอิสระ
jalf

4
ฉันเห็น ใช่ฉันพลาดขีดล่างที่หายไป
John Dibling

3
@JohnDibling ขึ้นโหวตสำหรับการอธิบายความละเอียดอ่อนระหว่างconst iteraterและconst_iterator.
legends2k

8

โชคไม่ดีที่วิธีการมากมายสำหรับคอนเทนเนอร์ STL ใช้ตัวทำซ้ำแทนที่จะใช้const_iteratorsเป็นพารามิเตอร์ ดังนั้นหากคุณมีconst_iteratorคุณจะไม่สามารถพูดว่า "แทรกองค์ประกอบก่อนองค์ประกอบที่ตัววนซ้ำนี้ชี้ไป" (การบอกว่าสิ่งนั้นไม่ได้เป็นการละเมิดแนวคิดในความคิดในความคิดของฉัน) หากคุณต้องการทำต่อไปที่คุณจะต้องแปลงเป็น iterator ไม่ใช่ const ใช้มาตรฐาน :: ล่วงหน้า ()หรือเพิ่ม :: ถัดไป () เช่น. เพิ่ม :: ถัดไป (container.begin () มาตรฐาน :: ระยะทาง (container.begin () the_const_iterator_we_want_to_unconst)) ถ้าภาชนะที่เป็นมาตรฐาน :: รายการแล้วเวลาทำงานสำหรับการโทรที่จะเป็นO (n)

ดังนั้นกฎสากลในการเพิ่ม const ไม่ว่าจะเป็น "ตรรกะ" ในการทำเช่นนั้นจึงมีความเป็นสากลน้อยกว่าเมื่อพูดถึงคอนเทนเนอร์ STL

อย่างไรก็ตามบูสต์คอนเทนเนอร์ใช้ const_iterators (เช่น boost :: unordered_map :: erase ()) ดังนั้นเมื่อคุณใช้บูสต์คอนเทนเนอร์คุณจะ "บีบรัด" ได้ ยังไงก็ตามใครทราบหรือไม่ว่าคอนเทนเนอร์ STL จะได้รับการแก้ไขหรือไม่?


1
อาจเป็นเรื่องของความคิดเห็น ในกรณีของvectorและdequeใส่องค์ประกอบหนึ่งเลิก iterators constที่มีอยู่ทั้งหมดซึ่งไม่มาก แต่ฉันเห็นประเด็นของคุณ การดำเนินการดังกล่าวได้รับการปกป้องโดย container const-ness ไม่ใช่ตัวทำซ้ำ และฉันสงสัยว่าทำไมไม่มีฟังก์ชันการแปลงตัววนซ้ำ const-to-nonconst ในอินเทอร์เฟซคอนเทนเนอร์มาตรฐาน
Potatoswatter

คุณถูกต้อง Potatoswatter ฉันเป็นคนจัดหมวดหมู่มันเป็นเรื่องของความคิดเห็นสำหรับคอนเทนเนอร์การเข้าถึงโดยสุ่มและcontainer.begin () + (the_const_iterator_we_want_to_unconst - container.begin ())เป็นO (1)อยู่ดี ฉันยังสงสัยว่าทำไมไม่มีฟังก์ชันการแปลงสำหรับคอนเทนเนอร์การเข้าถึงแบบไม่สุ่ม แต่อาจมีเหตุผลที่ดี? คุณรู้หรือไม่ว่ามีเหตุผลบางประการที่ฟังก์ชันสำหรับคอนเทนเนอร์การเข้าถึงแบบไม่สุ่มไม่ใช้const_iterators ?
Magnus Andermo

"การพูดเช่นนั้นไม่ได้เป็นการละเมิดแนวคิดในความคิดของฉัน" - นี่เป็นความคิดเห็นที่น่าสนใจมากฉันมีความคิดต่อไปนี้ในหัวข้อนี้ ด้วยคำแนะนำง่ายๆเราสามารถพูดได้int const * foo; int * const foo;และint const * const foo;ทั้งสามอย่างนั้นถูกต้องและมีประโยชน์โดยแต่ละอย่างมีวิธีการของตัวเอง std::vector<int> const barควรจะเหมือนกับอันที่สอง แต่น่าเสียดายที่มักจะถือว่าเหมือนอันที่สาม ต้นตอของปัญหาคือเราไม่สามารถบอกได้ว่าstd::vector<int const> bar;เมื่อไหร่หมายความว่าไม่มีทางที่จะได้รับผลเช่นเดียวint const *foo;กับเวกเตอร์
dgnuff

6

ใช้const_iteratorทุกครั้งที่ทำได้ใช้iteratorเมื่อคุณไม่มีทางเลือกอื่น


4

ตัวอย่างที่รันได้น้อยที่สุด

ตัวทำซ้ำที่ไม่เป็นองค์ประกอบทำให้คุณสามารถแก้ไขสิ่งที่ชี้ไปที่:

std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();
*it = 1;
assert(v[0] == 1);

ตัววนซ้ำคงไม่:

const std::vector<int> v{0};
std::vector<int>::const_iterator cit = v.begin();
// Compile time error: cannot modify container with const_iterator.
//*cit = 1;

ดังที่แสดงไว้ด้านบนv.begin()มีการconstโอเวอร์โหลดและส่งกลับอย่างใดอย่างหนึ่งiteratorหรือconst_iteratorขึ้นอยู่กับ const-ness ของตัวแปร container:

กรณีทั่วไปที่const_iteratorป๊อปอัปคือเมื่อthisใช้ภายในconstเมธอด:

class C {
    public:
        std::vector<int> v;
        void f() const {
            std::vector<int>::const_iterator it = this->v.begin();
        }
        void g(std::vector<int>::const_iterator& it) {}
};

constทำให้thisconst ซึ่งทำให้this->vconst

โดยปกติคุณสามารถลืมมันได้autoแต่ถ้าคุณเริ่มส่งผ่านตัวทำซ้ำเหล่านั้นคุณจะต้องนึกถึงลายเซ็นวิธีการเหล่านั้น

เช่นเดียวกับ const และ non-const คุณสามารถแปลงจาก non-const เป็น const ได้อย่างง่ายดาย แต่ไม่ใช่วิธีอื่น:

std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();

// non-const to const.
std::vector<int>::const_iterator cit = it;

// Compile time error: cannot modify container with const_iterator.
//*cit = 1;

// Compile time error: no conversion from const to no-const.
//it = ci1;

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


0

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


0

ตกลงให้ฉันอธิบายด้วยตัวอย่างง่าย ๆ ก่อนโดยไม่ต้องใช้ตัววนซ้ำคงที่ถือว่าเรามีคอลเลกชันของจำนวนเต็มสุ่มคอลเลกชัน "randomData"

    for(vector<int>::iterator i = randomData.begin() ; i != randomData.end() ; ++i)*i = 0;
for(vector<int>::const_iterator i = randomData.begin() ; i!= randomData.end() ; ++i)cout << *i;

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

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