Memilih jenis Variabel Indeks

11

Kami menggunakan tipe Integer mewakili variabel indeks sebagian besar waktu. Tetapi dalam beberapa situasi, kita terpaksa memilih

std::vector<int> vec;
....

for(int i = 0; i < vec.size(); ++i)
....

Ini akan menyebabkan kompiler untuk meningkatkan peringatan bahwa penggunaan campuran variabel yang ditandatangani / tidak ditandatangani. jika saya membuat variabel indeks sebagai for( size_t i = 0; i < vec.size(); i++ ), (atau an unsigned int) itu akan memilah masalah.

Ketika datang lebih spesifik untuk menggunakan jenis windows, sebagian besar Windows API berhadapan dengan DWORD (yang diketik lama sebagai unsigned).

Jadi ketika saya menggunakan iterasi yang sama, akan kembali menimbulkan peringatan yang sama. Sekarang jika saya menulis ulang sebagai

DWORD dwCount;
....

for(DWORD i = 0; i < dwCount; ++i)
....

Saya menemukan ini agak aneh. Mungkin masalah dengan persepsi.

Saya setuju bahwa kita seharusnya menggunakan jenis variabel indeks yang sama untuk menghindari masalah rentang dapat terjadi dengan variabel indeks. Misalnya, jika kita menggunakan

_int64 i64Count; // 
....

for(_int64 i = 0; i < i64Count; ++i)
....

Tetapi dalam kasus DWORD, atau bilangan bulat tak bertanda, apakah ada masalah dalam penulisan ulang sebagai

for(int i = 0; (size_t)i < vec.size(); ++i)

Bagaimana sebagian besar orang bekerja dengan masalah serupa?

sarat
sumber
4
Mengapa Anda menggunakan integer yang ditandatangani untuk mewakili indeks? Itu seperti menggunakan vektor bilangan bulat untuk menyimpan string.
Šimon Tóth
3
@Let_Me_Be: Karena itu membuat kondisi batas lebih mudah untuk diuji. Misalnya pada hitungan mundur ke nol, tes kurang dari sebelum menjalankan loop body tidak dapat bekerja dengan nilai yang tidak ditandatangani. Demikian pula hitungan hingga maksimum tidak berfungsi. Tentu saja dalam kasus itu bilangan bulat yang ditandatangani tidak akan berfungsi baik (karena tidak dapat mewakili nilai sebesar itu).
Yttrill
"Ini akan menyebabkan kompiler untuk meningkatkan peringatan bahwa penggunaan campuran variabel yang ditandatangani / tidak ditandatangani." Itu hanya satu dari dua masalah yang harus Anda hadapi. Dalam banyak kasus std::size_tperingkatnya lebih tinggi dari int (atau bahkan panjang). Jika ukuran vektor melebihi std::numeric_limits<int>::max(), Anda akan menyesal telah menggunakan int.
Adrian McCarthy

Jawaban:

11

vektor memiliki typedef yang memberitahu Anda jenis yang benar untuk digunakan: -

for(std::vector<int>::size_type i = 0; i < thing.size(); ++i)
{
}

Meskipun hampir selalu didefinisikan sebagai size_t tetapi Anda tidak dapat mengandalkan itu

JohnB
sumber
8
Tidak benar-benar peningkatan keterbacaan, IMHO.
Doc Brown
Tidak, tetapi karena ini satu-satunya cara untuk mengetahui jenis apa yang tepat digunakan untuk indeks ke dalam vektor, itu tidak terlalu penting ...
JohnB
4
Saat ini di c ++ 11 cukup gunakan auto
JohnB
6
@JohnB Maksudmu seperti auto i = 0? Itu sama sekali tidak membantu, imenjadi seorang int.
zenith
1
Keterbacaan dapat ditingkatkan, dengan using index_t = std::vector<int>::size_type;.
Toby Speight
4
std::vector<int> vec;

for(int i = 0; i < vec.size(); ++i)

Gunakan iterator untuk ini, bukan forloop.

Untuk yang lain, selama tipe variabel memiliki ukuran yang sama, static_castharus berfungsi dengan baik (yaitu DWORDuntuk int16_t)

Demian Brecht
sumber
2
for (std::vector<int>::iterator i = vec.begin(); i != vec.end(); ++i)adalah rasa sakit untuk menulis. Memiliki for (auto i = vec.begin();...jauh lebih mudah dibaca. Tentu saja, foreachjuga di C ++ 11.
David Thornley
3

Kasus yang Anda gambarkan adalah salah satu hal yang saya sukai di C ++ juga. Tetapi saya telah belajar untuk hidup dengan itu, baik dengan menggunakan

for( size_t i = 0; i < vec.size(); i++ )

atau

for( int i = 0; i < (int)vec.size(); i++ )

(tentu saja, yang terakhir hanya ketika tidak ada risiko mendapatkan beberapa overflow int).

Doc Brown
sumber
3

Alasannya memperingatkan Anda tentang perbandingan antara yang ditandatangani dan yang tidak ditandatangani adalah karena nilai yang ditandatangani kemungkinan akan dikonversi menjadi tidak ditandatangani, yang mungkin bukan yang Anda harapkan.

Dalam contoh Anda (membandingkan intdengan size_t), intakan secara implisit dikonversi ke size_t(kecuali intentah bagaimana memiliki rentang yang lebih besar dari size_t). Jadi, jika intnegatif, kemungkinan akan lebih besar dari nilai yang Anda bandingkan dengan karena sampulnya. Ini tidak akan menjadi masalah jika indeks Anda tidak pernah negatif, tetapi Anda akan tetap mendapatkan peringatan itu.

Sebaliknya, gunakan tipe unsigned (seperti unsigned int, size_t, atau, seperti John B merekomendasikan , std::vector<int>::size_type) untuk variabel indeks Anda:

for(unsigned int i = 0; i < vec.size(); i++)

Berhati-hatilah saat menghitung mundur:

for(unsigned int i = vec.size()-1; i >= 0; i--) // don't do this!

Di atas tidak akan berfungsi karena i >= 0selalu benar ketika itidak ditandatangani. Sebagai gantinya, gunakan " operator panah " untuk loop yang menghitung mundur:

for (unsigned int i = vec.size(); i-- > 0; )
    vec[i] = ...;

Seperti jawaban lain tunjukkan, Anda biasanya ingin menggunakan iterator untuk melintasi a vector. Berikut sintaks C ++ 11:

for (auto i = vec.begin(); i != vec.end(); ++i)
Joey Adams
sumber
1
Ini masih berisiko bahwa unsigned intukurannya tidak cukup besar.
Adrian McCarthy
2

Opsi baru untuk C ++ 11, Anda dapat melakukan hal-hal seperti berikut ini

for(decltype(vec.size()) i = 0; i < vec.size(); ++i) {...}

dan

for(decltype(dWord) i = 0; i < dWord; ++i) {...}

Meskipun ia mengulangi sedikit lebih banyak daripada yang akan dilakukan for-loop dasar, itu tidak terlalu panjang lebar seperti cara pra-'11 menentukan nilai, dan menggunakan pola ini secara konsisten akan bekerja untuk sebagian besar, jika tidak semua, kemungkinan istilah yang Anda inginkan ingin membandingkan, yang membuatnya bagus untuk refactoring kode. Ia bahkan berfungsi untuk kasus sederhana seperti ini:

int x = 3; int final = 32; for(decltype(final) i = x; i < final; ++i)

Selain itu, sementara Anda harus menggunakan autosetiap kali Anda mengatur ike beberapa nilai cerdas (seperti vec.begin()), decltypebekerja ketika Anda menetapkan ke konstan seperti nol, di mana otomatis hanya akan menyelesaikannya intkarena 0 adalah bilangan bulat integer sederhana.

Sejujurnya, saya ingin melihat mekanisme kompiler untuk memperluas autopenentuan-tipe untuk loop incrementers untuk melihat nilai yang dibandingkan.

matthias
sumber
1

Saya menggunakan gips untuk int, seperti pada for (int i = 0; i < (int)v.size(); ++i). Ya, itu jelek. Saya menyalahkan itu pada desain bodoh dari perpustakaan standar di mana mereka memutuskan untuk menggunakan bilangan bulat bertanda untuk mewakili ukuran. (Untuk .. apa? Perluas rentang satu bit?)

zvrba
sumber
1
Dalam situasi apa ukuran kumpulan sesuatu yang negatif akan bermakna? Menggunakan bilangan bulat yang tidak ditandatangani untuk berbagai ukuran koleksi sepertinya pilihan yang masuk akal , bagi saya. Terakhir saya periksa, mengambil panjang string jarang mengembalikan hasil negatif juga ...
CVn
1
Dalam situasi apa akan berarti untuk tes sederhana seperti if(v.size()-1 > 0) { ... }mengembalikan true untuk wadah kosong? Masalahnya adalah bahwa ukuran juga sering digunakan dalam aritmatika, khususnya. dengan wadah berbasis indeks, yang meminta masalah mengingat mereka tidak ditandatangani. Pada dasarnya, menggunakan tipe yang tidak ditandatangani untuk hal lain selain 1) manipulasi bitwise, atau 2) aritmatika modular membutuhkan masalah.
zvrba
2
Poin yang bagus. Meskipun saya tidak benar-benar melihat poin dari contoh khusus Anda (saya mungkin hanya akan menulis if(v.size() > 1) { ... }karena itu membuat maksudnya lebih jelas, dan sebagai bonus tambahan masalah ditandatangani / tidak ditandatangani menjadi batal), saya melihat bagaimana dalam beberapa kasus tertentu kesesuaian mungkin berguna. Saya berdiri dikoreksi.
CVn
1
@ Michael: Saya setuju itu adalah contoh yang dibuat-buat. Namun, saya sering menulis algoritma dengan loop bersarang: untuk (i = 0; i <v.size () - 1; ++ i) untuk (j = i + 1; j <v.size (); ++ j) .. jika v kosong, loop luar dijalankan (size_t) -1 kali. Jadi saya juga harus memeriksa v.empty () sebelum loop atau melemparkan v.size () ke tipe yang ditandatangani, yang menurut saya pribadi merupakan solusi yang jelek. Saya memilih pemain karena LOC lebih sedikit, tidak ada if () s => kemungkinan lebih sedikit untuk kesalahan. (Juga, dalam komplemen ke-2, konversi arus balik menghasilkan angka negatif, sehingga loop tidak mengeksekusi sama sekali.)
zvrba
Memperluas rentang dengan 1 bit adalah (dan terus menjadi) sangat berguna dalam sistem 16-bit.
Adrian McCarthy