ฉันสงสัยว่าทำไม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_iterators และดังนั้นจึงจะเรียกว่ามี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 ซึ่งจะคืนค่าตัววนซ้ำคงที่