ฉันสงสัยว่าทำไมcbegin
และcend
ได้รับการแนะนำใน C ++ 11?
มีกรณีอะไรเมื่อเรียกวิธีการเหล่านี้สร้างความแตกต่างจาก const overloads begin
และend
?
ฉันสงสัยว่าทำไมcbegin
และcend
ได้รับการแนะนำใน C ++ 11?
มีกรณีอะไรเมื่อเรียกวิธีการเหล่านี้สร้างความแตกต่างจาก const overloads begin
และend
?
คำตอบ:
มันค่อนข้างง่าย พูดว่าฉันมีเวกเตอร์:
std::vector<int> vec;
ฉันเติมด้วยข้อมูลบางส่วน จากนั้นฉันอยากได้ตัววนซ้ำไป อาจผ่านพวกเขาไปรอบ ๆ อาจจะstd::for_each
:
std::for_each(vec.begin(), vec.end(), SomeFunctor());
ใน C ++ 03 SomeFunctor
มีอิสระที่จะสามารถแก้ไขพารามิเตอร์ที่ได้รับ แน่นอนว่าSomeFunctor
สามารถใช้พารามิเตอร์ตามค่าหรือตามconst&
แต่ไม่มีวิธีที่จะทำให้แน่ใจได้ ไม่ได้ทำอะไรโง่ ๆ แบบนี้:
const std::vector<int> &vec_ref = vec;
std::for_each(vec_ref.begin(), vec_ref.end(), SomeFunctor());
ตอนนี้เราแนะนำcbegin/cend
:
std::for_each(vec.cbegin(), vec.cend(), SomeFunctor());
ตอนนี้เรามีการรับรองทางวากยสัมพันธ์ที่SomeFunctor
ไม่สามารถแก้ไของค์ประกอบของเวกเตอร์ (แน่นอนว่าไม่มีนักแสดง) เราได้รับอย่างชัดเจนconst_iterator
s และดังนั้นจึงจะเรียกว่ามีSomeFunctor::operator()
const int &
หากใช้เป็นพารามิเตอร์int &
C ++ จะออกข้อผิดพลาดของคอมไพเลอร์
C ++ 17 มีวิธีแก้ไขปัญหาที่หรูหรากว่านี้: std::as_const
. อย่างน้อยก็สง่างามเมื่อใช้ช่วงแบบfor
:
for(auto &item : std::as_const(vec))
สิ่งนี้จะคืนค่า a const&
ไปยังวัตถุที่ถูกจัดเตรียมไว้
std::cbegin/cend
ฟังก์ชั่นฟรีในแบบที่std::begin/std::end
มีอยู่ มันเป็นการกำกับดูแลของคณะกรรมการ หากมีฟังก์ชั่นเหล่านั้นอยู่นั่นก็เป็นวิธีการใช้งาน
std::cbegin/cend
จะถูกเพิ่มใน C ++ 14 ดูen.cppreference.com/w/cpp/iterator/begin
for(auto &item : std::as_const(vec))
เทียบเท่ากับfor(const auto &item : vec)
?
const
ข้อมูลอ้างอิง Nicol มองว่าคอนเทนเนอร์เป็น const ดังนั้นauto
อนุมานconst
ข้อมูลอ้างอิง IMO auto const& item
นั้นง่ายและชัดเจนยิ่งขึ้น มันไม่มีความชัดเจนว่าทำไมถึงstd::as_const()
ดีที่นี่; ฉันเห็นว่ามันจะมีประโยชน์เมื่อส่งบางสิ่งที่ไม่ใช่const
รหัสทั่วไปซึ่งเราไม่สามารถควบคุมประเภทที่ใช้งานได้ แต่ด้วยช่วงfor
- เราทำได้ดังนั้นดูเหมือนว่าจะเพิ่มเสียงรบกวนให้ฉันที่นั่น
นอกเหนือจากที่ Nicol Bolas พูดไว้ในคำตอบของเขาแล้วลองพิจารณาauto
คำค้นหาใหม่:
auto iterator = container.begin();
ด้วยauto
ไม่มีวิธีที่จะทำให้แน่ใจว่าbegin()
จะส่งกลับผู้ประกอบการคงที่สำหรับการอ้างอิงภาชนะไม่คงที่ ดังนั้นตอนนี้คุณทำ:
auto const_iterator = container.cbegin();
const_iterator
เป็นเพียงตัวระบุอื่น รุ่นทั้งใช้การค้นหาของ typedefs สมาชิกปกติหรือdecltype(container)::iterator
decltype(container)::const_iterator
const_iterator
กับauto
: เขียนเทมเพลตฟังก์ชั่นเสริมที่เรียกว่าmake_const
มีคุณสมบัติอาร์กิวเมนต์วัตถุ
ใช้สิ่งนี้เป็น usecase ในทางปฏิบัติ
void SomeClass::f(const vector<int>& a) {
auto it = someNonConstMemberVector.begin();
...
it = a.begin();
...
}
การมอบหมายล้มเหลวเนื่องจากit
เป็นตัววนซ้ำที่ไม่เป็นอันตราย หากคุณใช้ cbegin ในตอนแรกตัววนซ้ำจะมีชนิดที่ถูกต้อง
จากhttp://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1674.pdf :
เพื่อให้โปรแกรมเมอร์สามารถรับ const_iterator ได้โดยตรงจากแม้แต่คอนเทนเนอร์ที่ไม่ใช่ const
พวกเขายกตัวอย่างนี้
vector<MyType> v;
// fill v ...
typedef vector<MyType>::iterator iter;
for( iter it = v.begin(); it != v.end(); ++it ) {
// use *it ...
}
อย่างไรก็ตามเมื่อ traversal ของคอนเทนเนอร์มีไว้สำหรับการตรวจสอบเท่านั้นมันเป็นวิธีปฏิบัติที่นิยมใช้ในการใช้ const_iterator เพื่อให้คอมไพเลอร์วินิจฉัยการละเมิดความถูกต้องของ const
โปรดทราบว่ากระดาษทำงานยังกล่าวถึงแม่แบบของอะแดปเตอร์ซึ่งตอนนี้ได้รับการสรุปstd::begin()
และstd::end()
และยังทำงานกับอาร์เรย์พื้นเมือง ที่เกี่ยวข้องstd::cbegin()
และstd::cend()
หายไปอยากรู้อยากเห็น ณ เวลานี้ แต่พวกเขาอาจจะเพิ่ม
เพิ่งสะดุดกับคำถามนี้ ... ฉันรู้ว่ามันเป็นคำตอบ alredy และมันเป็นเพียงโหนดด้านข้าง ...
auto const it = container.begin()
เป็นประเภทที่แตกต่างกันไปแล้ว auto it = container.cbegin()
ความแตกต่างสำหรับint[5]
(ใช้ตัวชี้ซึ่งฉันรู้ว่าไม่มีวิธีการเริ่มต้น แต่แสดงความแตกต่างอย่างชัดเจน ... แต่จะทำงานใน c ++ 14 สำหรับstd::cbegin()
และstd::cend()
ซึ่งเป็นหลักสิ่งที่ควรใช้เมื่ออยู่ที่นี่) ...
int numbers = array[7];
const auto it = begin(numbers); // type is int* const -> pointer is const
auto it = cbegin(numbers); // type is int const* -> value is const
iterator
และconst_iterator
มีความสัมพันธ์ทางมรดกและการแปลงโดยนัยเกิดขึ้นเมื่อเปรียบเทียบกับหรือกำหนดให้กับประเภทอื่น
class T {} MyT1, MyT2, MyT3;
std::vector<T> MyVector = {MyT1, MyT2, MyT3};
for (std::vector<T>::const_iterator it=MyVector.begin(); it!=MyVector.end(); ++it)
{
// ...
}
การใช้cbegin()
และcend()
จะเพิ่มประสิทธิภาพในกรณีนี้
for (std::vector<T>::const_iterator it=MyVector.cbegin(); it!=MyVector.cend(); ++it)
{
// ...
}
const
ประโยชน์หลักคือประสิทธิภาพ (ซึ่งไม่ใช่: เป็นรหัสที่ถูกต้องและปลอดภัย) แต่ในขณะที่คุณมีจุด (A) auto
ทำให้มันไม่ใช่ประเด็น; (B) ในการพูดคุยเกี่ยวกับประสิทธิภาพคุณพลาดสิ่งสำคัญที่คุณควรทำที่นี่: แคชตัวend
วนซ้ำโดยการประกาศสำเนาในสภาพ init ของfor
ลูป & เปรียบเทียบกับมันแทนที่จะได้รับสำเนาใหม่โดย ค่าสำหรับการวนซ้ำทุกครั้ง นั่นจะทำให้จุดของคุณดีขึ้น : P
const
สามารถช่วยให้ได้ประสิทธิภาพที่ดีขึ้นอย่างแน่นอนไม่ใช่เพราะเวทมนตร์บางอย่างในconst
คำหลักนั้นเอง แต่เนื่องจากคอมไพเลอร์สามารถเปิดใช้งานการเพิ่มประสิทธิภาพบางอย่างหากรู้ว่าข้อมูลจะไม่ถูกแก้ไขซึ่งจะเป็นไปไม่ได้ ตรวจสอบบิตนี้จากการพูดคุยของ Jason Turner สำหรับตัวอย่างสดของสิ่งนี้
const
สามารถนำไปสู่ประโยชน์ด้านประสิทธิภาพ ในกรณีที่บางคนที่อ่านข้อความนี้อาจคิดว่า "ฉันจะไม่รบกวนการเพิ่มconst
หากโค้ดที่สร้างขึ้นไม่ได้รับผลกระทบ แต่อย่างใด" ซึ่งไม่เป็นความจริง
มันง่าย cbegin ส่งคืนตัววนซ้ำค่าคงที่ซึ่งเริ่มต้นส่งคืนตัววนซ้ำ
เพื่อความเข้าใจที่ดีขึ้นลองทำสองสถานการณ์ที่นี่
สถานการณ์ - 1:
#include <iostream>
using namespace std;
#include <vector>
int main(int argc, char const *argv[])
{
std::vector<int> v;
for (int i = 1; i < 6; ++i)
{
/* code */
v.push_back(i);
}
for(auto i = v.begin();i< v.end();i++){
*i = *i + 5;
}
for (auto i = v.begin();i < v.end();i++){
cout<<*i<<" ";
}
return 0;
}
สิ่งนี้จะทำงานเพราะที่นี่ตัววนซ้ำ i ไม่คงที่และสามารถเพิ่มขึ้นได้ 5
ตอนนี้เรามาใช้ cbegin และ cend แทนพวกมันว่าเป็นตัววนซ้ำค่าคงที่ - 2:
#include <iostream>
using namespace std;
#include <vector>
int main(int argc, char const *argv[])
{
std::vector<int> v;
for (int i = 1; i < 6; ++i)
{
/* code */
v.push_back(i);
}
for(auto i = v.cbegin();i< v.cend();i++){
*i = *i + 5;
}
for (auto i = v.begin();i < v.end();i++){
cout<<*i<<" ";
}
return 0;
}
สิ่งนี้จะไม่ทำงานเนื่องจากคุณไม่สามารถอัปเดตค่าโดยใช้ cbegin และ cend ซึ่งจะคืนค่าตัววนซ้ำคงที่