W lekcji 4.3 -- Rozmiary obiektów i operator sizeof zauważyliśmy, że C++ ma gwarancje minimalnej wielkości dla każdego z podstawowych typów. Jednakże rzeczywisty rozmiar tych typów może się różnić w zależności od kompilatora i architektury.
Ta zmienność została dozwolona, aby int i double typy danych mogły być ustawione na rozmiar, który maksymalizuje wydajność w danej architekturze. Na przykład komputer 32-bitowy będzie zazwyczaj w stanie przetwarzać 32 bity danych jednocześnie. W takich przypadkach szerokość int byłaby prawdopodobnie ustawiona na 32 bity, ponieważ jest to „naturalny” rozmiar danych, na których operuje procesor (i prawdopodobnie jest najbardziej wydajny).
Przypomnienie
Liczba bitów używanych przez typ danych nazywana jest jego szerokością. Szerszy typ danych to taki, który wykorzystuje więcej bitów, a węższy typ danych to taki, który wykorzystuje mniej bitów.
Ale co się stanie, gdy chcemy, aby nasz 32-bitowy procesor modyfikował wartość 8-bitową (taką jak char) lub wartość 16-bitową? Niektóre procesory 32-bitowe (takie jak 32-bitowe procesory x86) mogą bezpośrednio manipulować wartościami 8-bitowymi lub 16-bitowymi. Jednakże jest to często wolniejsze niż manipulowanie wartościami 32-bitowymi! Inne 32-bitowe procesory (takie jak 32-bitowe procesory PowerPC) mogą działać tylko na wartościach 32-bitowych, a do manipulowania węższymi wartościami należy zastosować dodatkowe sztuczki.
Promocja numeryczna
Ponieważ C++ został zaprojektowany tak, aby był przenośny i wydajny w szerokim zakresie architektur, projektanci języka nie chcieli zakładać, że dany procesor będzie w stanie efektywnie manipulować wartościami węższymi niż naturalny rozmiar danych dla tego Procesor.
Aby pomóc stawić czoła temu wyzwaniu, w języku C++ zdefiniowano kategorię konwersji typów, nieformalnie zwaną numeric promotions. A promocja numeryczna to konwersja typów pewnych węższych typów liczbowych (takich jak a char) na pewne szersze typy liczbowe (zwykle int lub double), które można efektywnie przetwarzać.
Wszystkie promocje liczbowe zachowują wartość. A konwersja zachowująca wartość (zwana także bezpieczna konwersja) to taka, w której każdą możliwą wartość źródłową można przekonwertować na taką samą wartość typu docelowego.
Ponieważ promocje są bezpieczne, kompilator będzie swobodnie stosować promocję liczbową w razie potrzeby i nie wyświetli przy tym ostrzeżenia.
Promocja liczbowa zmniejsza redundancja
Awans numeryczny rozwiązuje także inny problem. Rozważmy przypadek, w którym chcesz napisać funkcję wyświetlającą wartość typu int:
#include <iostream>
void printInt(int x)
{
std::cout << x << '\n';
}Chociaż jest to proste, co się stanie, jeśli chcemy mieć także możliwość wydrukowania wartości typu short lub typu char? Gdyby nie istniała konwersja typów, musielibyśmy napisać inną funkcję drukującą dla short i drugą dla char. I nie zapomnij o kolejnej wersji dla unsigned char, signed char, unsigned short, wchar_t, char8_t, char16_t, I char32_t! Można zobaczyć, jak szybko staje się to niewykonalne.
Tutaj z pomocą przychodzi promocja numeryczna: możemy napisać funkcje posiadające int i/lub double parametry (takie jak funkcja printInt() powyższa). Ten sam kod można następnie wywołać z argumentami typów, które można numerycznie promować w celu dopasowania typów parametrów funkcji.
Kategorie promocji liczbowej
Reguły promocji numerycznej są podzielone na dwie podkategorie: integral promotions i floating point promotions. Tylko konwersje wymienione w tych kategoriach są uznawane za promocje liczbowe.
Promocje zmiennoprzecinkowe
Zaczniemy od łatwiejszej.
Korzystając z promocji zmiennoprzecinkowej , wartość typu float można przekonwertować na wartość type double.
Oznacza to, że możemy napisać funkcję, która przyjmuje double , a następnie wywołuje ją z double lub a float wartością:
#include <iostream>
void printDouble(double d)
{
std::cout << d << '\n';
}
int main()
{
printDouble(5.0); // no conversion necessary
printDouble(4.0f); // numeric promotion of float to double
return 0;
}W drugim wywołaniu printDouble(), instrukcja float literal 4.0f jest promowana do a double, tak aby typ argumentu odpowiadał typowi funkcji parametr.
Integracyjne promocje
Zasady promocji integralnej są bardziej skomplikowane.
Korzystając z reguł promocji integralnej , można dokonać następujących konwersji:
- signed char lub Signed Short można przekonwertować na int.
- unsigned char, char8_t i unsigned short można przekonwertować na int, jeśli int może pomieścić cały zakres typ lub w przeciwnym razie unsigned int.
- Jeśli znak jest domyślnie podpisany, stosuje się do powyższych reguł konwersji znaku ze znakiem. Jeśli domyślnie jest to znak bez znaku, stosuje się do powyższych zasad konwersji znaków bez znaku.
- bool można przekonwertować na int, przy czym false staje się 0, a true staje się 1.
Zakładając 8-bitowy bajt i int rozmiar 4 bajtów lub większy (co jest typowe obecnie), powyższe oznacza w zasadzie, że bool, char, signed char, unsigned char, signed short, I unsigned short wszystkie zostaną awansowane do int.
Istnieje kilka innych integralnych zasad awansu, które są stosowane rzadziej. Można je znaleźć pod adresem https://en.cppreference.com/w/cpp/language/implicit_conversion#Integral_promotion.
W większości przypadków pozwala nam to napisać funkcję przyjmującą parametr int , a następnie używać jej z szeroką gamą innych typów całkowitych. Na przykład:
#include <iostream>
void printInt(int x)
{
std::cout << x << '\n';
}
int main()
{
printInt(2);
short s{ 3 }; // there is no short literal suffix, so we'll use a variable for this one
printInt(s); // numeric promotion of short to int
printInt('a'); // numeric promotion of char to int
printInt(true); // numeric promotion of bool to int
return 0;
}Warto tu zwrócić uwagę na dwie rzeczy. Po pierwsze, w niektórych architekturach (np. z 2-bajtowymi intami) możliwe jest promowanie niektórych typów całkowitych bez znaku do unsigned int zamiast int.
. Po drugie, niektóre węższe typy bez znaku (takie jak unsigned char) mogą być promowane do większych typów całkowitych ze znakiem (takich jak int). Zatem chociaż promocja integralna zachowuje wartość, niekoniecznie zachowuje podpis (ze znakiem/bez znaku) typu.
Nie wszystkie konwersje rozszerzające są promocjami numerycznymi
Niektóre konwersje typu rozszerzającego (takie jak char Do short lub int Do long) nie są uważane za promocje numeryczne w C++ (są to numeric conversions, co omówimy wkrótce w lekcja 10.3 -- Konwersje liczbowe). Dzieje się tak, ponieważ takie konwersje nie pomagają w osiągnięciu celu, jakim jest konwersja mniejszych typów na większe typy, które można przetwarzać wydajniej.
Rozróżnienie to ma głównie charakter akademicki. Jednak w niektórych przypadkach kompilator będzie faworyzował promocje numeryczne zamiast konwersji numerycznych. Zobaczymy przykłady, w których ma to znaczenie, gdy będziemy omawiać rozwiązywanie przeciążenia funkcji (w nadchodzącej lekcji 11.3 — Rozwiązywanie przeciążenia funkcji i dopasowania niejednoznaczne).

