Jawaban:
Sederhana saja. Katakanlah saya punya vektor:
std::vector<int> vec;
Saya mengisinya dengan beberapa data. Lalu saya ingin mendapatkan beberapa iterator untuk itu. Mungkin melewati mereka. Mungkin untuk std::for_each
:
std::for_each(vec.begin(), vec.end(), SomeFunctor());
Di C ++ 03, SomeFunctor
bebas untuk dapat memodifikasi parameter yang didapatnya. Tentu, SomeFunctor
bisa mengambil parameternya berdasarkan nilai atau const&
, tetapi tidak ada cara untuk memastikannya . Bukan tanpa melakukan sesuatu yang konyol seperti ini:
const std::vector<int> &vec_ref = vec;
std::for_each(vec_ref.begin(), vec_ref.end(), SomeFunctor());
Sekarang, kami memperkenalkan cbegin/cend
:
std::for_each(vec.cbegin(), vec.cend(), SomeFunctor());
Sekarang, kami memiliki jaminan sintaksis yang SomeFunctor
tidak dapat memodifikasi elemen-elemen vektor (tentu saja, tanpa menggunakan konstanta). Kami secara eksplisit mendapatkan const_iterator
s, dan karenanya SomeFunctor::operator()
akan dipanggil dengan const int &
. Jika parameter itu diambil sebagai int &
, C ++ akan mengeluarkan kesalahan kompiler.
C ++ 17 memiliki solusi yang lebih elegan untuk masalah ini: std::as_const
. Yah, setidaknya elegan saat menggunakan berbasis rentang for
:
for(auto &item : std::as_const(vec))
Ini hanya mengembalikan a const&
ke objek yang disediakan.
std::cbegin/cend
fungsi gratis seperti yang std::begin/std::end
ada. Itu adalah pengawasan oleh komite. Jika fungsi-fungsi itu memang ada, itu biasanya akan menjadi cara untuk menggunakannya.
std::cbegin/cend
akan ditambahkan dalam C ++ 14. Lihat en.cppreference.com/w/cpp/iterator/begin
for(auto &item : std::as_const(vec))
setara dengan for(const auto &item : vec)
?
const
referensi. Nicol memandang wadah sebagai const, jadi auto
menyimpulkan const
referensi. IMO auto const& item
lebih mudah dan lebih jelas. Tidak jelas mengapa std::as_const()
bagus di sini; Saya bisa melihatnya akan berguna ketika melewati sesuatu yang bukan const
untuk kode generik di mana kita tidak bisa mengontrol tipe yang digunakan, tetapi dengan range- for
, kita bisa, jadi sepertinya ada noise tambahan di sana.
Di luar apa yang dikatakan Nicol Bolas dalam jawabannya , pertimbangkan auto
kata kunci baru :
auto iterator = container.begin();
Dengan auto
, tidak ada cara untuk memastikan bahwa begin()
mengembalikan operator konstan untuk referensi kontainer tidak konstan. Jadi sekarang Anda lakukan:
auto const_iterator = container.cbegin();
const_iterator
hanyalah pengidentifikasi lain. Versi tidak menggunakan pencarian dari typedefs anggota biasa decltype(container)::iterator
atau decltype(container)::const_iterator
.
const_iterator
dengan auto
: Menulis fungsi template tambahan yang disebut make_const
lolos argumen objek.
Anggap ini sebagai usecase praktis
void SomeClass::f(const vector<int>& a) {
auto it = someNonConstMemberVector.begin();
...
it = a.begin();
...
}
Penugasan gagal karena it
merupakan iterator nonconst. Jika Anda menggunakan cbegin pada awalnya, iterator akan memiliki tipe yang tepat.
Dari http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1674.pdf :
sehingga seorang programmer dapat langsung mendapatkan const_iterator bahkan dari wadah non-const
Mereka memberi contoh ini
vector<MyType> v;
// fill v ...
typedef vector<MyType>::iterator iter;
for( iter it = v.begin(); it != v.end(); ++it ) {
// use *it ...
}
Namun, ketika sebuah traversal kontainer hanya dimaksudkan untuk inspeksi, itu adalah praktik yang umumnya disukai untuk menggunakan const_iterator untuk mengizinkan kompiler untuk mendiagnosis pelanggaran konstitusionalitas.
Perhatikan bahwa kertas kerja juga menyebutkan template adaptor, yang sekarang telah selesai std::begin()
dan std::end()
dan juga bekerja dengan array asli. Yang sesuai std::cbegin()
dan std::cend()
anehnya hilang pada saat ini, tetapi mereka mungkin juga ditambahkan.
Baru saja menemukan pertanyaan ini ... Saya tahu itu sudah menjawab dan itu hanya simpul samping ...
auto const it = container.begin()
adalah tipe yang berbeda auto it = container.cbegin()
perbedaan untuk int[5]
(menggunakan pointer, yang saya tahu tidak memiliki metode mulai tetapi menunjukkan dengan baik perbedaan ... tetapi akan bekerja di c ++ 14 untuk std::cbegin()
dan std::cend()
, yang pada dasarnya apa yang harus digunakan ketika ada di sini) ...
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
dan const_iterator
memiliki hubungan pewarisan dan konversi implisit terjadi jika dibandingkan dengan atau ditugaskan ke jenis lainnya.
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)
{
// ...
}
Menggunakan cbegin()
dan cend()
akan meningkatkan kinerja dalam hal ini.
for (std::vector<T>::const_iterator it=MyVector.cbegin(); it!=MyVector.cend(); ++it)
{
// ...
}
const
manfaat utama adalah kinerja (yang bukan: itu kode yang secara semantik benar dan aman). Tapi, sementara Anda ada benarnya, (A) auto
membuat itu bukan masalah; (B) dalam berbicara tentang kinerja, Anda melewatkan hal utama yang harus Anda lakukan di sini: cache end
iterator dengan mendeklarasikan salinannya dalam kondisi awal for
loop, & bandingkan dengan itu, alih-alih mendapatkan salinan baru dengan nilai untuk setiap iterasi. Itu akan membuat poin Anda lebih baik. : P
const
pasti dapat membantu mencapai kinerja yang lebih baik, bukan karena beberapa keajaiban dalam const
kata kunci itu sendiri tetapi karena kompiler dapat mengaktifkan beberapa optimasi jika tahu bahwa data tidak akan dimodifikasi, yang tidak akan mungkin dilakukan sebaliknya. Periksa bit ini dari ceramah Jason Turner untuk contoh langsung tentang ini.
const
dapat (hampir secara tidak langsung) mengarah pada manfaat kinerja; kalau-kalau seseorang membaca ini mungkin berpikir "Saya tidak akan repot menambahkan const
jika kode yang dihasilkan tidak terpengaruh dengan cara apa pun", yang tidak benar.
sederhana, cbegin mengembalikan iterator konstan di mana mulai mengembalikan iterator
untuk pemahaman yang lebih baik mari kita ambil dua skenario di sini
skenario 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;
}
ini akan berjalan karena di sini iterator saya tidak konstan dan dapat bertambah 5
sekarang mari kita gunakan cbegin dan cend menunjukkan mereka sebagai skenario iterators konstan - 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;
}
ini tidak akan berhasil, karena Anda tidak dapat memperbarui nilai menggunakan cbegin dan cend yang mengembalikan iterator konstan