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 }; // zainicjuj zmienną zmiennoprzecinkową za pomocą 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 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ł float:
#include <iostream>
#include <cstring>
int main()
{
int n { 3 }; // tutaj wartość int 3
float f {}; // Oto nasza zmienna zmiennoprzecinkowa
std::memcpy(&f, &n, sizeof(float)); // skopiuj bity z n do f
std::cout << f << '\n'; // drukuj f (zawierający bity z 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 wartość 3 niejawnie konwertowana na typ double
d = 6; // int wartość 6 niejawnie przekonwertowany na typ doubleGdy typ zwracanej wartości różni się od zadeklarowanego typu zwracanego przez funkcję:
float doSomething()
{
return 3.0; // wartość double 3.0 pośrednio przekonwertowana na typ float
}Podczas używania niektórych operatorów binarnych z operandami różnych typów:
double division{ 4.0 / 3 }; // int wartość 3 niejawnie konwertowana na typ doubleW przypadku użycia wartości innej niż logiczna w instrukcji if:
if (5) // int wartość 5 niejawnie przekonwertowany na typ bool
{
}Gdy argument przekazany do funkcji jest innego typu niż parametr funkcji:
void doSomething(long l)
{
}
doSomething(3); // int wartość 3 niejawnie konwertowana na typ 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”. 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 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 }; // inicjowanie nawiasów klamrowych uniemożliwia konwersje skutkujące utratą danychNawet 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!

