Вибір типу змінних індексу


11

Ми використовуємо тип Integer, які представляють змінні індексу більшу частину часу. Але в деяких ситуаціях ми змушені вибирати

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

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

Це змусить компілятора підвищити попередження про змішане використання підписаних / неподписаних змінних. якщо я зроблю змінну індексу як for( size_t i = 0; i < vec.size(); i++ ), (або an unsigned int), вона розібрає проблеми.

Коли більш специфічно використовувати типи Windows, більшість API API мають справу з DWORD (який typedef-ed як довго не підписаний).

Тож коли я використовую подібну ітерацію, знову викличуть те саме попередження. Тепер, якщо я перепишу це як

DWORD dwCount;
....

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

Я вважаю це трохи дивним. Це може бути проблема сприйняття.

Я погоджуюся, що ми повинні використовувати один і той же тип змінної індексу, щоб уникнути проблем діапазону з змінними індексу. Наприклад, якщо ми використовуємо

_int64 i64Count; // 
....

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

Але у випадку з DWORD або непідписаними цілими чистами проблемами, якщо переписати його як

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

Як більшість людей працює з подібними питаннями?


4
Чому б ви використовували підписане ціле число для представлення індексу? Це як використовувати вектор цілих чисел для зберігання рядка.
Šimon Tóth

3
@Let_Me_Be: Тому що це полегшує тестування граничних умов. Наприклад, при відліку до нуля, менший тест перед виконанням корпусу циклу не може працювати з непідписаним значенням. Аналогічно підрахунок до максимуму не працює. Звичайно, у цьому випадку підписане ціле число також не буде працювати (оскільки воно не може представляти настільки велике значення).
Іттріл

"Це призведе до того, що компілятор підніме попередження про змішане використання підписаних / неподписаних змінних." Це лише одна з двох проблем, з якими вам доведеться зіткнутися. У багатьох випадках std::size_tце вищий ранг, ніж int (або навіть довгий). Якщо розмір вектора коли-небудь перевищує std::numeric_limits<int>::max(), ви пошкодуєте, що використовували int.
Адріан Маккарті

Відповіді:


11

вектор має typedef, який вказує правильний тип використання: -

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

Це майже завжди визначено як size_t, але ви не можете на це покластися


8
Насправді не поліпшення читабельності, ІМХО.
Док Браун

Ні, але оскільки це єдиний спосіб сказати, який правильний тип використовувати для індексу у векторі, це насправді не має значення ...
JohnB

4
Цими днями в c ++ 11 просто використовуйте auto
JohnB

6
@JohnB Ви маєте на увазі, як auto i = 0? Це зовсім не допомагає iстати стати int.
зеніт

1
Читання можна покращити за допомогою using index_t = std::vector<int>::size_type;.
Toby Speight

4
std::vector<int> vec;

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

Для цього використовуйте ітератор, а не forцикл.

Для інших, якщо тип змінної має однаковий розмір, він static_castповинен працювати добре (тобто DWORDдо int16_t)


2
for (std::vector<int>::iterator i = vec.begin(); i != vec.end(); ++i)- це біль писати. Наявність for (auto i = vec.begin();...- це набагато легше читати. Звичайно, foreachтакож є в C ++ 11.
Девід Торнлі

3

Випадок, який ви описали, - одна з речей, які мені також не подобаються в C ++. Але я навчився жити з цим, або використовуючи

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

або

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

(звичайно, останнє лише тоді, коли немає ризику отримати якийсь інт переповнення).


3

Причина, по якій він попереджає вас про порівняння між підписаним та непідписаним, полягає в тому, що підписане значення, ймовірно, буде перетворене в ненаписане, що може бути не таким, як ви очікуєте.

У вашому прикладі (порівняно intз size_t) intбуде неявно перетворено size_t(якщо тільки intякимось чином не має більшого діапазону size_t). Таким чином, якщо значення intнегативне, воно, швидше за все, буде більшим за значення, з яким ви порівнюєте його через обертання. Це не буде проблемою, якщо ваш індекс ніколи не буде негативним, але ви все одно отримаєте це попередження.

Замість цього використовуйте непідписаний тип (наприклад unsigned int, size_tчи, як рекомендує John B , std::vector<int>::size_type) для індексної змінної:

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

Будьте уважні під час відліку, хоча:

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

Вищезазначене не спрацює, оскільки i >= 0це завжди вірно, коли він iне підписаний. Замість цього скористайтеся " оператором зі стрілками " для циклів, що відраховують:

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

Як вказують інші відповіді, зазвичай ви хочете використовувати ітератор для переходу через a vector. Ось синтаксис C ++ 11:

for (auto i = vec.begin(); i != vec.end(); ++i)

1
Це все ще ризикує отримати unsigned intнедостатньо великий розмір, щоб утримати розмір.
Адріан Маккарті

2

Нова опція для C ++ 11, ви можете робити наступні дії

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

і

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

Хоча це повторюється трохи більше, ніж це було б для базового циклу, це не так довго, як багаторічні способи визначення значень, і використання цього шаблону послідовно працюватиме для більшості, якщо не всіх можливих термінів Хочете порівняти з, що робить його чудовим для рефакторингу коду. Він працює навіть для простих випадків, таких як цей:

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

Крім того, хоча ви повинні використовувати autoщоразу, коли ви встановлюєте iякесь інтелектуальне значення (наприклад vec.begin()), воно decltypeпрацює, коли ви встановлюєте константу, як нуль, де авто просто вирішує це значення, intоскільки 0 - це простий цілий літерал.

Якщо чесно, я хотів би побачити механізм компілятора для розширення визначення autoтипу для інкреметрів циклу, щоб переглянути значення, яке порівнюється.


1

Я використовую ролик для int, як в for (int i = 0; i < (int)v.size(); ++i). Так, це некрасиво. Я звинувачую це в дурному дизайні стандартної бібліотеки, де вони вирішили використовувати непідписані цілі числа для представлення розмірів. (Для того, щоб .. що? Розширити діапазон на один біт?)


1
У якій ситуації розмір негативу нічого негативного не має значення? Використання непідписаних цілих чисел для різних розмірів колекції здається мені розумним вибором. Востаннє я перевірив, взявши довжину рядка, рідко повертався негативний результат ...
CVn,

1
У якій ситуації було б важливим простий тест, такий як if(v.size()-1 > 0) { ... }повернення true для порожнього контейнера? Проблема полягає в тому, що розміри також часто використовуються в арифметиці, особливо. з контейнерами на основі індексу, що задає проблеми, враховуючи те, що вони не підписані. В основному, використання непідписаних типів для чого-небудь іншого, ніж 1) побітові маніпуляції або 2) модульна арифметика викликає проблеми.
zvrba

2
гарна думка. Хоча я справді не бачу сенсу у вашому конкретному прикладі (я б, мабуть, просто писав, if(v.size() > 1) { ... }оскільки це робить наміри більш чіткими, і як додатковий бонус питання підписаних / неподписаних стає недійсним), я розумію, як у деяких конкретних випадках підписання може бути корисним. Я стою виправлений.
CVn

1
@Michael: Я згоден, що це був надуманий приклад. І все-таки я часто пишу алгоритми з вкладеними циклами: for (i = 0; i <v.size () - 1; ++ i) для (j = i + 1; j <v.size (); ++ j) .. якщо v порожній, зовнішній цикл виконується (size_t) -1 раз. Тому я або повинен перевірити v.empty () перед циклом, або передати v.size () на підписаний тип, обидва з яких я особисто думаю, що це некрасиві способи вирішення. Я вибираю амплуа, оскільки менше LOC, ні, якщо () s => менше можливостей для помилки. (Також у другому додатку переповнення перетворення повертає від'ємне число, тому цикл взагалі не виконується.)
zvrba

Розширення діапазону на 1 біт було (і продовжує залишатися) дуже корисним у 16-бітних системах.
Адріан Маккарті
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.