Да, и резултатът е това, което бихте очаквали.
Нека го разбием.
Каква е стойността на r
в този момент? Е, долният поток е добре дефиниран и води до това, че r
приема максималната си стойност до момента на стартиране на сравнението. std::size_t
няма конкретни известни граници, но можем да направим разумни предположения за обхвата му в сравнение с този на int
:
std::size_t
е целочисленият тип без знак на резултата от оператора sizeof. [..] std::size_t
може да съхранява максималния размер на теоретично възможен обект от всякакъв тип (включително масив).
И само за да го премахнем от пътя, изразът -1
е унарен -
приложен към литерала 1
и има тип int
във всяка система:
[C++11: 2.14.2/2]:
Типът на целочислен литерал е първият от съответния списък в Таблица 6, в който може да бъде представена неговата стойност. [..]
(Няма да цитирам целия текст, който описва как прилагането на унарно -
към int
води до int
, но го прави.)
Повече от разумно е да се предположи, че в повечето системи int
няма да може да задържи std::numeric_limits<std::size_t>::max()
.
Сега, какво се случва с тези операнди?
[C++11: 5.10/1]:
Операторите ==
(равно на) и !=
(не е равно на) имат същите семантични ограничения, преобразувания и тип резултат като релационните оператори, с изключение на техния по-нисък приоритет и резултат с истинска стойност. [..]
[C++11: 5.9/2]:
Обичайните аритметични преобразувания се извършват върху операнди от аритметичен или изброен тип. [..]
Нека разгледаме тези „обичайни аритметични преобразувания“:
[C++11: 5/9]:
Много двоични оператори, които очакват операнди от аритметичен или изброяващ тип, причиняват преобразувания и дават типове резултати по подобен начин. Целта е да се получи общ тип, който е и типът на резултата.
Този модел се нарича обичайни аритметични преобразувания, които се дефинират както следва:
- Ако някой от операндите е от тип изброяване с обхват (7.2), не се извършват преобразувания; ако другият операнд няма същия тип, изразът е неправилно формиран.
- Ако някой от операндите е от тип
long double
, другият се преобразува в long double`.
- В противен случай, ако някой от операндите е
double
, другият трябва да бъде преобразуван в double
.
- В противен случай, ако някой от операндите е
float
, другият трябва да бъде преобразуван в float
.
- Otherwise, the integral promotions (4.5) shall be performed on both operands.59 Then the following rules shall be applied to the promoted operands:
- If both operands have the same type, no further conversion is needed.
- В противен случай, ако и двата операнда имат типове цели числа със знак или и двата имат типове цели числа без знак, операндът с тип по-малък ранг на преобразуване на цяло число ще бъде преобразуван в типа на операнда с по-висок ранг.
- В противен случай, ако операндът с тип цяло число без знак има ранг, по-голям или равен на ранга на типа на другия операнд, операндът с тип цяло число със знак ще бъде преобразуван в типа на операнда с тип цяло число без знак.
- В противен случай, ако типът на операнда с тип цяло число със знак може да представи всички стойности на типа на операнда с тип цяло число без знак, операндът с тип цяло число без знак трябва да бъде преобразуван в типа на операнд с тип цяло число със знак.
- В противен случай и двата операнда ще бъдат преобразувани в целочислен тип без знак, съответстващ на типа на операнда с целочислен тип със знак.
Подчертах пасажа, който влиза в сила тук, и относно защо:
[C++11: 4.13/1]
: Всеки тип цяло число има ранг на преобразуване на цяло число, дефиниран както следва
- [..]
- Рангът на
long long int
трябва да бъде по-висок от ранга на long int
, който трябва да бъде по-висок от ранга на int
, който трябва да бъде по-висок от ранга на short int
, който трябва да бъде по-висок от ранга на signed char
.
- Рангът на всеки тип цяло число без знак трябва да бъде равен на ранга на съответния тип цяло число със знак.
- [..]
Всички интегрални типове, дори тези с фиксирана ширина, са съставени от стандартните интегрални типове; следователно, логично, std::size_t
трябва да бъде unsigned long long
, unsigned long
или unsigned int
.
Ако std::size_t
е unsigned long long
или unsigned long
, тогава рангът на std::size_t
е по-голям от ранга на unsigned int
и, следователно, също и на int
.
Ако std::size_t
е unsigned int
, рангът на std::size_t
е равен на ранга на unsigned int
и следователно също на int
.
Така или иначе, според обичайните аритметични преобразувания, операндът със знак се преобразува в типа на операнда без знак (и най-важното, не обратното!). Сега, какво включва това преобразуване?
[C++11: 4.7/2]:
Ако типът местоназначение е без знак, получената стойност е най-малкото цяло число без знак, съответстващо на цяло число източник (по модул 2n, където n е броят на битовете, използвани за представяне на типа без знак). [ Забележка: При представяне с допълване от две това преобразуване е концептуално и няма промяна в модела на битовете (ако има не е съкращаване). —крайна бележка]
[C++11: 4.7/3]:
Ако типът местоназначение е подписан, стойността е непроменена, ако може да бъде представена в типа местоназначение (и ширина на битовото поле); в противен случай стойността е дефинирана от изпълнението.
Това означава, че std::size_t(-1)
е еквивалентно на std::numeric_limits<std::size_t>::max()
; от решаващо значение е стойността n в горната клауза да се отнася до броя на битовете, използвани за представяне на типа unsigned, а не типа източник. В противен случай щяхме да правим std::size_t((unsigned int)-1)
, което изобщо не е едно и също нещо, може да е много порядъци по-малко от желаната от нас стойност!
Наистина, сега, когато знаем, че всички реализации са добре дефинирани, можем да тестваме тази стойност:
std::cout << (std::size_t(-1) == std::numeric_limits<size_t>::max()) << '\n';
// "1"
И само за да илюстрирам мнението си от по-рано, за моята 64-битова система:
std::cout << std::is_same<unsigned long, std::size_t>::value << '\n';
std::cout << std::is_same<unsigned long, unsigned int>::value << '\n';
std::cout << std::hex << std::showbase
<< std::size_t(-1) << ' '
<< std::size_t(static_cast<unsigned int>(-1)) << '\n';
// "1"
// "0"
// "0xffffffffffffffff 0xffffffff"
person
Lightness Races in Orbit
schedule
10.12.2014
r--
не е недостиг, въпреки че е разумно да се отнася към него неофициално по този начин. - person Keith Thompson   schedule 10.12.2014const bool result = (r == (size_t)-1);
. Само за да сте сигурни, че компилира интерпретира-1
катоsize_t
, а неr
като някаква променлива от друг тип. Но вашият вариант също може да е правилен, просто трябва да погледнете стандарта. - person ST3   schedule 11.12.2014