Wprowadziliśmy konwersję typów na lekcji 4.12 -- Wprowadzenie do konwersji typów i static_cast. Podsumowując najważniejsze punkty tej lekcji:
- Proces konwersji danych z jednego typu na inny nazywa się „konwersją typu”.
- Niejawna konwersja typów jest wykonywana automatycznie przez kompilator, gdy wymagany jest jeden typ danych, ale dostarczany jest inny typ danych.
- Jawna konwersja typu jest wymagana przy użyciu operatora rzutowania, takiego jak
static_cast. - Konwersje nie powodują zmiany konwertowanych danych. Zamiast tego proces konwersji wykorzystuje te dane jako dane wejściowe i generuje przekonwertowany wynik.
- Podczas konwertowania wartości na wartość innego typu proces konwersji tworzy obiekt tymczasowy typu docelowego, w którym przechowywany jest wynik konwersji.
W pierwszej połowie tego rozdziału przyjrzymy się bliżej działaniu konwersji typów. Zaczniemy od niejawnych konwersji w tej lekcji, a jawnych konwersji typu (casting) w następnej lekcji 10.6 -- Jawna konwersja typów (casting) i static_cast. Ponieważ konwersja typów jest stosowana wszędzie, ważne jest pewne zrozumienie tego, co dzieje się pod maską, gdy konieczna jest konwersja. Wiedza ta jest również istotna przy zrozumieniu działania przeciążonych funkcji (funkcji, które mogą mieć taką samą nazwę jak inne funkcje).
Nota autora
W tym rozdziale skupimy się na konwersji wartości na inne typy wartości. Inne typy konwersji omówimy po wprowadzeniu wymaganych tematów (takich jak wskaźniki, referencje, dziedziczenie itp.).
Dlaczego potrzebne są konwersje
Wartość obiektu jest przechowywana jako sekwencja bitów, a typ danych obiektu mówi kompilatorowi, jak zinterpretować te bity na znaczące wartości. Różne typy danych mogą w różny sposób reprezentować „tę samą” wartość. Na przykład wartość całkowita 3 może być przechowywany jako plik binarny 0000 0000 0000 0000 0000 0000 0000 0011, natomiast wartość zmiennoprzecinkowa 3.0 może być przechowywany jako plik binarny 0100 0000 0100 0000 0000 0000 0000 0000.
Co więc się stanie, gdy zrobimy coś takiego?
float f{ 3 }; // initialize floating point variable with int 3W takim przypadku kompilator nie może po prostu skopiować bitów używanych do reprezentacji int wartości 3 do pamięci przeznaczonej dla float zmienną f. Jeśli tak się stanie, to kiedy f (który ma typ float) została oceniona, bity te będą interpretowane jako a float zamiast inti kto wie co float wartość, z którą skończymy!
Na marginesie…
Poniższy program faktycznie drukuje int wartości 3 jakby to był A float:
#include <iostream>
#include <cstring>
int main()
{
int n { 3 }; // here's int value 3
float f {}; // here's our float variable
std::memcpy(&f, &n, sizeof(float)); // copy the bits from n into f
std::cout << f << '\n'; // print f (containing the bits from n)
return 0;
}Daje to następujący wynik:
4.2039e-45
Zamiast tego wartość całkowita 3 należy przeliczyć na równoważną wartość zmiennoprzecinkową 3.0, które można następnie zapisać w przeznaczonej dla nich pamięci f (używając reprezentacji bitowej dla float wartości 3.0) .
Kiedy następuje niejawna konwersja typu
Niejawna konwersja typu (tzw automatyczna konwersja typów lub przymus) jest wykonywane automatycznie przez kompilator, gdy wyrażenie pewnego typu zostanie dostarczone w kontekście, w którym oczekiwany jest inny typ. Zdecydowana większość konwersji typów w C++ to niejawne konwersje typów. Na przykład niejawna konwersja typów ma miejsce we wszystkich następujących przypadkach:
Podczas inicjalizacji (lub przypisania wartości) zmiennej o wartości innego typu danych:
double d{ 3 }; // int value 3 implicitly converted to type double
d = 6; // int value 6 implicitly converted to type doubleGdy typ zwracanej wartości różni się od zadeklarowanego typu zwracanego przez funkcję:
float doSomething()
{
return 3.0; // double value 3.0 implicitly converted to type float
}Podczas używania niektórych operatorów binarnych z operandami różnych typów:
double division{ 4.0 / 3 }; // int value 3 implicitly converted to type doubleW przypadku użycia wartości innej niż logiczna w instrukcji if:
if (5) // int value 5 implicitly converted to type bool
{
}Gdy argument przekazany do funkcji jest innego typu niż parametr funkcji:
void doSomething(long l)
{
}
doSomething(3); // int value 3 implicitly converted to type longSkąd więc kompilator wie, jak przekonwertować wartość na inny typ?
Standardowe konwersje
Jako część języka podstawowego, standard C++ definiuje zbiór reguł konwersji znanych jako „konwersje standardowe”. The standardowe konwersje określ sposób, w jaki różne typy podstawowe (i niektóre typy złożone, w tym tablice, odniesienia, wskaźniki i wyliczenia) są konwertowane na inne typy w tej samej grupie.
Począwszy od C++23, istnieje 14 różnych standardowych konwersji. Można je z grubsza pogrupować w 5 ogólnych kategorii:
| Kategoria | Znaczenie | Link |
|---|---|---|
| Promocje liczbowe | Konwersje małych typów całkowitych na int lub unsigned int oraz float Do double. | 10.2 -- Promocja zmiennoprzecinkowa i całkowa |
| Konwersje liczbowe | Inne konwersje całkowe i zmiennoprzecinkowe, które nie są promocjami. | 10.3 -- Konwersje liczbowe |
| Konwersje kwalifikacyjne | Konwersje, które dodają lub usuń const lub volatile. | |
| Transformacje wartości | Konwersje zmieniające kategorię wartości wyrażenia | 12.2 — Kategorie wartości (lwartości i rwartości) |
| Konwersja wskaźników | Konwersje z std::nullptr na typy wskaźników lub typy wskaźników na inne typy wskaźników |
Na przykład konwersja wartości int wartość do A float należy do kategorii konwersji numerycznych, więc kompilator, aby wykonać taką konwersję, kompilator musi po prostu zastosować int Do float reguły konwersji numerycznej.
Konwersje liczbowe i promocje liczbowe są najważniejszymi z tych kategorii i omówimy je bardziej szczegółowo w nadchodzących lekcjach.
Dla zaawansowanych czytelników
Oto pełna lista standardowych konwersji:
| Kategoria | Konwersja standardowa | Description | Również Zobacz |
|---|---|---|---|
| Transformacja wartości | Lwartość na wartość | Konwertuje wyrażenie lwartości na wyrażenie rwartości | 12.2 — Kategorie wartości (lwartości i rwartości) |
| Transformacja wartości | Tablica na wskaźnik | Konwertuje tablicę w stylu C na wskaźnik na pierwszy element tablicy (czyli tablicę zanik) | 17.8 -- Zanikanie tablicy w stylu C |
| Transformacja wartości | Funkcja na wskaźnik | Konwertuje funkcję na wskaźnik funkcji | 20.1 -- Wskaźniki funkcji |
| Transformacja wartości | Tymczasowa materializacja | Konwertuje wartość na obiekt tymczasowy | |
| Kwalifikacja konwersja | Kwalifikacja konwersja | Dodaje lub usuwa const lub volatile z typów | |
| Promocje liczbowe | Integracyjne promocje | Konwertuje mniejsze typy całkowite na int lub unsigned int | 10.2 -- Promocja zmiennoprzecinkowa i całkowa |
| Promocje liczbowe | Promocje zmiennoprzecinkowe | Konwertuje float Do double | 10.2 -- Promocja zmiennoprzecinkowa i całkowa |
| Konwersje liczbowe | Konwersje całkowe | Konwersje całkowe, które nie są całkowe promocje | 10.3 -- Konwersje liczbowe |
| Konwersje liczbowe | Konwersje zmiennoprzecinkowe | Konwersje zmiennoprzecinkowe, które nie są promocjami zmiennoprzecinkowymi | 10.3 -- Konwersje liczbowe |
| Konwersje liczbowe | Konwersje całkowo-zmiennoprzecinkowe | Konwertuje typy całkowite i zmiennoprzecinkowe | 10.3 -- Konwersje liczbowe |
| Konwersje liczbowe | Konwersje logiczne | Konwertuje wyliczenie całkowe, bez zakresu, wskaźnik lub wskaźnik na memver na bool | 4.10 - Wprowadzenie do instrukcji if |
| Konwersja wskaźników | Konwersja wskaźników | Konwertuje std::nullptr na wskaźnik lub wskaźnik na pusty wskaźnik lub klasę bazową | |
| Konwersja wskaźników | Wskaźnik na element konwersje | Konwertuje std::nullptr na wskaźnik na elementlub wskaźnik na element klasy bazowej na wskaźnik na element klasy pochodnej | |
| Konwersja wskaźników | Konwersja wskaźników funkcyjnych | Konwertuje wskaźnik na funkcję noexcept wskaźnik do funkcji |
Konwersja typu może się nie powieść
Gdy zostanie wywołana konwersja typu (niejawnie lub jawnie), kompilator określi, czy może przekonwertować wartość z bieżącego typu na żądany typ. Jeśli uda się znaleźć prawidłową konwersję, kompilator utworzy nową wartość żądanego typu.
Jeśli kompilator nie znajdzie akceptowalnej konwersji, kompilacja zakończy się błędem. Konwersja typów może zakończyć się niepowodzeniem z wielu powodów. Na przykład kompilator może nie wiedzieć, jak przekonwertować wartość między typem oryginalnym a typem żądanym.
Na przykład:
int main()
{
int x { "14" };
return 0;
}Ponieważ nie ma standardowej konwersji z literału ciągu „14” na int, kompilator zgłosi błąd. Na przykład GCC generuje błąd: prog.cc:3:13: error: invalid conversion from 'const char*' to 'int' [-fpermissive].
W innych przypadkach określone funkcje mogą uniemożliwiać niektóre kategorie konwersji. Na przykład:
int x { 3.5 }; // brace-initialization disallows conversions that result in data lossNawet jeśli kompilator wie, jak przekonwertować double wartość na int wartość, konwersje zawężające są niedozwolone podczas korzystania z inicjalizacji nawiasów klamrowych.
Istnieją również przypadki, w których kompilator może nie być w stanie określić, która z kilku możliwych konwersji typów jest najlepsza do użycia. Zobaczymy tego przykłady na lekcji 11.3 — Rozwiązywanie przeciążenia funkcji i dopasowania niejednoznaczne.
Pełny zestaw reguł opisujących sposób działania konwersji typów jest zarówno długi, jak i skomplikowany, a w większości przypadków konwersja typów „po prostu działa”. W następnym zestawie lekcji omówimy najważniejsze rzeczy, które musisz wiedzieć o standardowych konwersjach. Jeśli w jakimś nietypowym przypadku wymagane są bardziej szczegółowe informacje, pełne zasady są szczegółowo opisane w technicznej dokumentacji referencyjnej dotyczącej konwersji niejawnych.
Zaczynajmy!

