Apa cara paling efektif untuk mendapatkan indeks iterator dari std :: vector?

439

Saya mengulangi vektor dan membutuhkan indeks iterator saat ini menunjuk. AFAIK ini dapat dilakukan dengan dua cara:

  • it - vec.begin()
  • std::distance(vec.begin(), it)

Apa pro dan kontra dari metode ini?

cairol
sumber

Jawaban:

558

Saya lebih suka it - vec.begin()justru karena alasan sebaliknya yang diberikan oleh Naveen: jadi itu tidak akan dikompilasi jika Anda mengubah vektor menjadi daftar. Jika Anda melakukan ini selama setiap iterasi, Anda bisa dengan mudah mengubah algoritma O (n) menjadi algoritma O (n ^ 2).

Pilihan lain, jika Anda tidak melompat-lompat di dalam wadah selama iterasi, akan menjaga indeks sebagai penghitung lingkaran kedua.

Catatan: itadalah nama umum untuk iterator penampung std::container_type::iterator it;,.

UncleBens
sumber
3
Sepakat. Saya akan mengatakan bahwa tanda minus adalah yang terbaik, tetapi akan lebih baik untuk menjaga penghitung loop kedua daripada menggunakan std :: distance, tepatnya karena fungsi ini bisa lambat.
Steven Sudit
28
apa-apaan ini it?
Steinfeld
32
@ Seinfeld ini merupakan iterator. std::container_type::iterator it;
Matt Munson
2
Menambahkan penghitung putaran kedua adalah solusi yang jelas sehingga saya malu saya tidak memikirkannya.
Mordred
3
@Swapnil karena std::listtidak menawarkan akses langsung ke elemen berdasarkan posisi mereka, jadi jika Anda tidak dapat melakukannya list[5], Anda seharusnya tidak dapat melakukannya list.begin() + 5.
José Tomás Tocino
135

Saya lebih suka std::distance(vec.begin(), it)karena akan memungkinkan saya untuk mengubah wadah tanpa perubahan kode. Misalnya, jika Anda memutuskan untuk menggunakan std::listalih-alih std::vectoryang tidak memberikan iterator akses acak, kode Anda masih akan dikompilasi. Karena std :: distance mengambil metode optimal tergantung pada sifat iterator Anda tidak akan mengalami penurunan kinerja.

Naveen
sumber
50
Ketika Anda menggunakan wadah tanpa iterator akses acak, yang terbaik adalah tidak menghitung jarak seperti itu karena tidak efisien
Eli Bendersky
6
@ Eli: Saya setuju dengan itu, tetapi dalam kasus yang sangat khusus jika benar-benar diperlukan, maka kode itu tetap berfungsi.
Naveen
9
Saya pikir kode harus diubah pula jika wadah berubah - memiliki std :: daftar variabel bernama vecadalah berita buruk. Jika kode tersebut ditulis ulang menjadi generik, dengan menggunakan tipe wadah sebagai parameter templat, saat itulah kita dapat (dan harus) berbicara tentang penanganan iterator non-akses acak ;-)
Steve Jessop
1
Dan spesialisasi untuk wadah tertentu.
ScaryAardvark
19
@SteveJessop: Memiliki nama vektor vecjuga berita buruk.
River Tam
74

Seperti yang ditunjukkan UncleBens dan Naveen, ada alasan bagus untuk keduanya. Yang mana yang "lebih baik" tergantung pada perilaku apa yang Anda inginkan: Apakah Anda ingin menjamin perilaku waktu-konstan, atau Anda ingin kembali ke waktu linier bila diperlukan?

it - vec.begin()membutuhkan waktu yang konstan, tetapi operator -hanya didefinisikan pada iterator akses acak, jadi kode tidak akan dikompilasi sama sekali dengan iterator daftar, misalnya.

std::distance(vec.begin(), it) bekerja untuk semua jenis iterator, tetapi hanya akan menjadi operasi waktu konstan jika digunakan pada iterator akses acak.

Tidak ada yang "lebih baik". Gunakan yang melakukan apa yang Anda butuhkan.

jalf
sumber
1
Saya telah melakukan kesalahan ini di masa lalu. Menggunakan std :: distance pada dua std :: map iterators dan mengharapkannya menjadi O (N).
ScaryAardvark
6
@ ScaryAardvark: Bukankah maksud Anda mengharapkannya menjadi O (1)?
Jalf
12

Saya suka yang ini:, it - vec.begin()karena bagi saya itu jelas mengatakan "jarak dari awal". Dengan iterator kita terbiasa berpikir dalam hal aritmatika, jadi -tandanya adalah indikator yang paling jelas di sini.

Eli Bendersky
sumber
19
Lebih jelas menggunakan pengurangan untuk menemukan jarak daripada menggunakan, secara harfiah, kata distance?
Travis Gockel
4
@ Travis, bagi saya itu. Ini masalah selera dan kebiasaan. Kami mengatakan it++dan bukan sesuatu seperti itu std::increment(it), bukan? Bukankah itu juga dianggap kurang jelas?
Eli Bendersky
3
The ++Operator didefinisikan sebagai bagian dari urutan STL bagaimana kita kenaikan iterator. std::distancemenghitung jumlah elemen antara elemen pertama dan terakhir. Fakta bahwa -operator bekerja hanyalah sebuah kebetulan.
Travis Gockel
3
@MSalters: namun, kami menggunakan ++ :-)
Eli Bendersky
10

Jika Anda sudah dibatasi / hardcode algoritma Anda untuk menggunakan a std::vector::iteratordan std::vector::iteratorhanya, tidak masalah metode mana yang Anda akan gunakan. Algoritme Anda sudah dikonkretkan melampaui titik di mana memilih satu dari yang lain dapat membuat perbedaan. Mereka berdua melakukan hal yang persis sama. Ini hanya masalah preferensi pribadi. Saya pribadi akan menggunakan pengurangan eksplisit.

Sebaliknya, jika Anda ingin mempertahankan tingkat umum yang lebih tinggi dalam algoritme Anda, yaitu, untuk memungkinkan kemungkinan bahwa suatu hari di masa depan itu dapat diterapkan ke beberapa jenis iterator lain, maka metode terbaik tergantung pada niat Anda. . Itu tergantung pada seberapa ketat Anda ingin berkaitan dengan jenis iterator yang dapat digunakan di sini.

  • Jika Anda menggunakan pengurangan eksplisit, algoritme Anda akan dibatasi untuk kelas iterator yang agak sempit: iterator akses-acak. (Ini adalah apa yang Anda dapatkan sekarang dari std::vector)

  • Jika Anda menggunakan distance, algoritma Anda akan mendukung kelas iterator yang lebih luas: input iterator.

Tentu saja, menghitung distanceuntuk iterator non-akses-acak pada umumnya merupakan operasi yang tidak efisien (sementara, sekali lagi, untuk akses-acak itu seefisien pengurangan). Terserah Anda untuk memutuskan apakah algoritma Anda masuk akal untuk iterator non-acak-akses, efisiensi-bijaksana. Jika kerugian yang dihasilkan dalam efisiensi sangat menghancurkan hingga membuat algoritma Anda sama sekali tidak berguna, maka Anda sebaiknya tetap berpegang pada pengurangan, sehingga melarang penggunaan yang tidak efisien dan memaksa pengguna untuk mencari solusi alternatif untuk jenis iterator lainnya. Jika efisiensi dengan iterator non-akses acak masih dalam rentang yang dapat digunakan, maka Anda harus menggunakan distancedan mendokumentasikan fakta bahwa algoritma ini bekerja lebih baik dengan iterator akses acak.

Semut
sumber
4

Menurut http://www.cplusplus.com/reference/std/iterator/distance/ , karena vec.begin()merupakan iterator akses acak , metode jarak menggunakan -operator.

Jadi jawabannya adalah, dari sudut pandang kinerja, itu sama, tetapi mungkin menggunakan distance()lebih mudah untuk dipahami jika ada yang harus membaca dan memahami kode Anda.

Stéphane
sumber
3

Saya akan menggunakan -varian std::vectorhanya - cukup jelas apa yang dimaksud, dan kesederhanaan operasi (yang tidak lebih dari pengurangan pointer) diekspresikan oleh sintaks ( distance, di sisi lain, terdengar seperti pythagoras pada bacaan pertama, bukan?). Seperti yang ditunjukkan oleh UncleBen, ini -juga bertindak sebagai pernyataan statis jika vectorsuatu kasus diubah secara tidak sengaja list.

Juga saya pikir ini jauh lebih umum - tidak ada angka untuk membuktikannya. Argumen utama: it - vec.begin()lebih pendek dalam kode sumber - lebih sedikit pekerjaan mengetik, lebih sedikit ruang yang digunakan. Karena jelas bahwa jawaban yang tepat untuk pertanyaan Anda menjadi masalah selera, ini juga bisa menjadi argumen yang valid.

Alexander Gessler
sumber
0

Berikut adalah contoh untuk menemukan "semua" kejadian 10 beserta indeksnya. Kupikir ini akan sangat membantu.

void _find_all_test()
{
    vector<int> ints;
    int val;
    while(cin >> val) ints.push_back(val);

    vector<int>::iterator it;
    it = ints.begin();
    int count = ints.size();
    do
    {
        it = find(it,ints.end(), 10);//assuming 10 as search element
        cout << *it << " found at index " << count -(ints.end() - it) << endl;
    }while(++it != ints.end()); 
}
Batthula Srikanth
sumber