Namensräume
Varianten
Aktionen

Implizite Konvertierungen

Von cppreference.com
< cpp‎ | Sprache
 
 
C++ Sprache
Allgemeine Themen
Kontrollfluss
Bedingte Ausführungsaussagen
if
Iterationsanweisungen (Schleifen)
for
Bereichs-for (C++11)
Sprunganweisungen
Funktionen
Funktionsdeklaration
Lambda-Funktionsausdruck
inline-Spezifizierer
Dynamische Ausnahmespezifikationen (bis C++17*)
noexcept-Spezifizierer (C++11)
Ausnahmen
Namensräume
Typen
Spezifizierer
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Speicherdauer-Spezifizierer
Initialisierung
Ausdrücke
Alternative Darstellungen
Literale
Boolesch - Ganzzahl - Gleitkommazahl
Zeichen - String - nullptr (C++11)
Benutzerdefinierte (C++11)
Dienstprogramme
Attribute (C++11)
Typen
typedef-Deklaration
Typalias-Deklaration (C++11)
Umwandlungen
Implizite Konvertierungen
static_cast
const_cast
Speicherzuweisung
Klassen
Klassenspezifische Funktionseigenschaften
explicit (C++11)
static

Spezielle Member-Funktionen
Templates
Sonstiges
 
 

Implizite Konvertierungen werden durchgeführt, wenn ein Ausdruck des Typs T1 in einem Kontext verwendet wird, der diesen Typ nicht akzeptiert, aber einen anderen Typ T2 akzeptiert; insbesondere

  • wenn der Ausdruck als Argument beim Aufruf einer Funktion verwendet wird, die mit T2 als Parameter deklariert ist;
  • wenn der Ausdruck als Operand mit einem Operator verwendet wird, der T2 erwartet;
  • wenn ein neues Objekt vom Typ T2 initialisiert wird, einschließlich einer return-Anweisung in einer Funktion, die T2 zurückgibt;
  • wenn der Ausdruck in einer switch-Anweisung verwendet wird (T2 ist ein integraler Typ);
  • wenn der Ausdruck in einer if-Anweisung oder einer Schleife verwendet wird (T2 ist bool).

Das Programm ist nur dann wohlgeformt (kompiliert), wenn es eine eindeutige *implizite Konvertierungssequenz* von T1 nach T2 gibt.

Wenn es mehrere Überladungen der aufgerufenen Funktion oder des Operators gibt, entscheiden die Regeln der Überladungsauflösung, welche Überladung kompiliert wird, nachdem die implizite Konvertierungssequenz von T1 zu jedem verfügbaren T2 aufgebaut wurde.

Hinweis: In arithmetischen Ausdrücken wird der Zieltyp für die impliziten Konvertierungen der Operanden binärer Operatoren durch einen separaten Satz von Regeln bestimmt: übliche arithmetische Konvertierungen.

Inhalt

[bearbeiten] Reihenfolge der Konvertierungen

Die implizite Konvertierungssequenz besteht aus den folgenden Elementen, in dieser Reihenfolge:

1) eine oder keine *Standardkonvertierungssequenz*;
2) eine oder keine *benutzerdefinierte Konvertierung*;
3) eine oder keine *Standardkonvertierungssequenz* (nur wenn eine benutzerdefinierte Konvertierung verwendet wird).

Bei der Betrachtung von Argumenten für einen Konstruktor oder eine benutzerdefinierte Konvertierungsfunktion ist nur eine Standardkonvertierungssequenz erlaubt (andernfalls könnten benutzerdefinierte Konvertierungen effektiv verkettet werden). Bei der Konvertierung von einem Nicht-Klassen-Typ zu einem anderen Nicht-Klassen-Typ ist nur eine Standardkonvertierungssequenz erlaubt.

Eine Standardkonvertierungssequenz besteht aus den folgenden Elementen, in dieser Reihenfolge:

1) eine oder keine Konvertierung aus der folgenden Menge:
  • Lvalue-zu-rvalue-Konvertierung,
  • Array-zu-Zeiger-Konvertierung und
  • Funktion-zu-Zeiger-Konvertierung;
2) eine oder keine *numerische Promotion* oder *numerische Konvertierung*;
3) eine oder keine *Zeiger-auf-Funktion-Konvertierung*;
(seit C++17)
4) eine oder keine *Qualifizierungs-Konvertierung*.

Eine benutzerdefinierte Konvertierung besteht aus dem Aufruf eines oder keines expliziten, einargumentigen Konvertierungskonstruktors oder expliziten Konvertierungsfunktion.

Ein Ausdruck e kann implizit nach T2 konvertiert werden, wenn und nur wenn T2 aus e per Kopierinitialisierung initialisiert werden kann, d. h., wenn die Deklaration T2 t = e; für ein beliebiges temporäres t wohlgeformt (kompilierbar) ist. Beachten Sie, dass dies von der Direktinitialisierung (T2 t(e)) abweicht, bei der explizite Konstruktoren und Konvertierungsfunktionen zusätzlich berücksichtigt würden.

[bearbeiten] Kontextbezogene Konvertierungen

In den folgenden Kontexten wird der Typ bool erwartet, und die implizite Konvertierung wird durchgeführt, wenn die Deklaration bool t(e); wohlgeformt ist (d. h., eine explizite Konvertierungsfunktion wie explicit T::operator bool() const; berücksichtigt wird). Ein solcher Ausdruck e wird als *kontextbezogen nach bool konvertiert* bezeichnet.

  • der steuernde Ausdruck von if, while, for;
  • die Operanden der eingebauten logischen Operatoren !, && und ||;
  • der erste Operand des bedingten Operators ?:;
  • das Prädikat in einer static_assert-Deklaration;
  • der Ausdruck in einem noexcept-Spezifizierer;
  • der Ausdruck in einem explicit-Spezifizierer;
(seit C++20)
(seit C++11)

In den folgenden Kontexten wird ein kontextspezifischer Typ T erwartet, und der Ausdruck e vom Klassentyp E ist nur erlaubt, wenn

(bis C++14)
  • es genau einen Typ T unter den zulässigen Typen gibt, für den E nicht-explizite Konvertierungsfunktionen hat, deren Rückgabetypen (möglicherweise cv-qualifiziert) T oder Referenz auf (möglicherweise cv-qualifiziert) T sind, und
  • e implizit nach T konvertierbar ist.
(seit C++14)

Ein solcher Ausdruck e wird als *kontextbezogen implizit konvertiert* in den angegebenen Typ T bezeichnet. Beachten Sie, dass explizite Konvertierungsfunktionen nicht berücksichtigt werden, obwohl sie bei kontextbezogenen Konvertierungen nach bool berücksichtigt werden.(seit C++11)

  • das Argument des delete-Ausdrucks (T ist ein beliebiger Objektzeigertyp);
  • Ganzzahliger konstanten Ausdruck, bei dem eine literale Klasse verwendet wird (T ist ein beliebiger integrales oder un-scopiertes Aufzählungstyp, die ausgewählte benutzerdefinierte Konvertierungsfunktion muss constexpr sein);
  • der steuernde Ausdruck der switch-Anweisung (T ist ein beliebiger integrales oder Aufzählungstyp).
#include <cassert>
 
template<typename T>
class zero_init
{
    T val;
public:
    zero_init() : val(static_cast<T>(0)) {}
    zero_init(T val) : val(val) {}
    operator T&() { return val; }
    operator T() const { return val; }
};
 
int main()
{
    zero_init<int> i;
    assert(i == 0);
 
    i = 7;
    assert(i == 7);
 
    switch (i) {}     // error until C++14 (more than one conversion function)
                      // OK since C++14 (both functions convert to the same type int)
    switch (i + 0) {} // always okay (implicit conversion)
}

[bearbeiten] Werttransformationen

Werttransformationen sind Konvertierungen, die die Wertkategorie eines Ausdrucks ändern. Sie finden statt, wenn ein Ausdruck als Operand eines Operators erscheint, der einen Ausdruck einer anderen Wertkategorie erwartet.

  • Immer wenn ein glvalue als Operand eines Operators erscheint, der für diesen Operanden einen prvalue erwartet, werden die Standardkonvertierungen *lvalue-zu-rvalue*, *array-zu-zeiger* oder *funktion-zu-zeiger* angewendet, um den Ausdruck in einen prvalue zu konvertieren.
  • Sofern nicht anders angegeben, wird immer dann, wenn ein prvalue als Operand eines Operators erscheint, der für diesen Operanden einen glvalue erwartet, die *temporäre Materialisierungskonvertierung* angewendet, um den Ausdruck in einen xvalue zu konvertieren.
(seit C++17)

[bearbeiten] Lvalue-zu-rvalue-Konvertierung

Ein lvalue(bis C++11)Ein glvalue(seit C++11) eines beliebigen Nicht-Funktions-, Nicht-Array-Typs T kann implizit in einen rvalue(bis C++11)prvalue(seit C++11) konvertiert werden.

  • Wenn T kein Klassentyp ist, ist der Typ des rvalue(bis C++11)prvalue(seit C++11) die cv-unqualifizierte Version von T.
  • Andernfalls ist der Typ des rvalue(bis C++11)prvalue(seit C++11) T.

Wenn eine lvalue-zu-rvalue-Konvertierung für einen unvollständigen Typ erforderlich ist, ist das Programm ill-formed.

Angenommen, das Objekt, auf das sich der lvalue(bis C++11)glvalue(seit C++11) verweist, ist obj.

  • Wenn eine lvalue-zu-rvalue-Konvertierung innerhalb des Operanden von sizeof auftritt, wird der Wert, der in obj enthalten ist, nicht gelesen, da dieser Operator seinen Operanden nicht auswertet.
  • Das Ergebnis der Konvertierung ist der Wert, der in obj enthalten ist. Wenn einer von T und der Typ von obj ein vorzeichenbehafteter Ganzzahltyp und der andere der entsprechende vorzeichenlose Ganzzahltyp ist, ist das Ergebnis der Wert vom Typ T mit der gleichen Wertdarstellung wie obj.
(bis C++11)
  • Wenn eine lvalue-zu-rvalue-Konvertierung auf einen Ausdruck E angewendet wird, wird der Wert, der in obj enthalten ist, nicht gelesen, wenn
  • Das Ergebnis der Konvertierung wird wie folgt bestimmt:
  • Wenn T (möglicherweise cv-qualifiziert) std::nullptr_t ist, ist das Ergebnis eine Nullzeigerkonstante. obj wird von der Konvertierung nicht gelesen, daher gibt es keine Seiteneffekte, selbst wenn T volatile-qualifiziert ist, und der glvalue kann sich auf ein inaktives Mitglied einer Union beziehen.
  • Andernfalls, wenn T ein Klassentyp ist:
  • Die Konvertierung initialisiert temporär vom Typ T aus dem glvalue per Kopierinitialisierung, und das Ergebnis der Konvertierung ist ein prvalue für das temporäre Objekt.
(bis C++17)
  • Die Konvertierung initialisiert das Ergebnisobjekt aus dem glvalue per Kopierinitialisierung.
(seit C++17)
  • Andernfalls, wenn obj einen ungültigen Zeigerwert enthält, ist das Verhalten implementierungsabhängig.
  • Andernfalls, wenn die Bits in der Wertdarstellung von obj für den Typ von obj ungültig sind, ist das Verhalten undefiniert.
  • Andernfalls, wird obj gelesen, und(seit C++20) das Ergebnis ist der Wert, der in obj enthalten ist. Wenn einer von T und der Typ von obj ein vorzeichenbehafteter Ganzzahltyp und der andere der entsprechende vorzeichenlose Ganzzahltyp ist, ist das Ergebnis der Wert vom Typ T mit der gleichen Wertdarstellung wie obj.
(seit C++11)

Diese Konvertierung modelliert das Einlesen eines Wertes von einer Speicherstelle in ein CPU-Register.

[bearbeiten] Array-zu-Zeiger-Konvertierung

Ein lvalue oder rvalue vom Typ "Array von N T" oder "Array unbekannter Größe von T" kann implizit in einen prvalue vom Typ "Zeiger auf T" konvertiert werden. Wenn das Array ein prvalue ist, tritt eine temporäre Materialisierung auf.(seit C++17) Der resultierende Zeiger verweist auf das erste Element des Arrays (Details siehe Array-zu-Zeiger-Zerfall).

[bearbeiten] Funktion-zu-Zeiger-Konvertierung

Ein lvalue vom Funktionstyp kann implizit in einen prvalue Zeiger auf diese Funktion konvertiert werden. Dies gilt nicht für nicht-statische Memberfunktionen, da lvalues, die auf nicht-statische Memberfunktionen verweisen, nicht existieren.

Temporäre Materialisierung

Ein prvalue eines beliebigen vollständigen Typs T kann in einen xvalue desselben Typs T konvertiert werden. Diese Konvertierung initialisiert ein temporäres Objekt vom Typ T aus dem prvalue, indem der prvalue mit dem temporären Objekt als Ergebnisobjekt ausgewertet wird, und erzeugt einen xvalue, der das temporäre Objekt bezeichnet.

Wenn T ein Klassen- oder Arraytyp ist, muss er einen zugänglichen und nicht gelöschten Destruktor haben.

struct S { int m; };
int i = S().m; // member access expects glvalue as of C++17;
               // S() prvalue is converted to xvalue

Temporäre Materialisierung tritt in den folgenden Situationen auf:

Beachten Sie, dass die temporäre Materialisierung *nicht* auftritt, wenn ein Objekt aus einem prvalue desselben Typs initialisiert wird (per Direktinitialisierung oder Kopierinitialisierung): Ein solches Objekt wird direkt aus dem Initialisierer initialisiert. Dies gewährleistet eine "garantierte Kopierelision".

(seit C++17)

[bearbeiten] Ganzzahlige Promotion

prvalues kleiner ganzzahliger Typen (wie char) und un-scopierte Aufzählungstypen können in prvalues größerer ganzzahliger Typen (wie int) konvertiert werden. Insbesondere akzeptieren arithmetische Operatoren keine Typen, die kleiner als int sind, als Argumente, und ganzzahlige Promotions werden automatisch nach der lvalue-zu-rvalue-Konvertierung angewendet, falls zutreffend. Diese Konvertierung erhält immer den Wert.

Die folgenden impliziten Konvertierungen in diesem Abschnitt werden als *ganzzahlige Promotions* klassifiziert.

Beachten Sie, dass für einen gegebenen Quelltyp der Zieltyp der ganzzahligen Promotion eindeutig ist, und alle anderen Konvertierungen keine Promotions sind. Zum Beispiel wählt die Überladungsauflösung char -> int (Promotion) über char -> short (Konvertierung).

[bearbeiten] Promotion von ganzzahligen Typen

Ein prvalue vom Typ bool kann in einen prvalue vom Typ int konvertiert werden, wobei false zu 0 und true zu 1 wird.

Für einen prvalue val eines ganzzahligen Typs T außer bool:

1) Wenn val das Ergebnis einer lvalue-zu-rvalue-Konvertierung eines Bitfeldes ist,
  • val kann in einen prvalue vom Typ int konvertiert werden, wenn int alle Werte des Bitfeldes darstellen kann;
  • andernfalls kann val in unsigned int konvertiert werden, wenn unsigned int alle Werte des Bitfeldes darstellen kann;
  • andernfalls kann val gemäß den Regeln in Punkt (3) konvertiert werden.
2) Andernfalls (wenn val nicht aus einem Bitfeld konvertiert wird),
  • wenn T char8_t, (seit C++20)char16_t, char32_t oder (seit C++11)wchar_t ist, kann val gemäß den Regeln in Punkt (3) konvertiert werden;
  • andernfalls, wenn der Integer-Konvertierungsrang von T niedriger ist als der Rang von int:
  • val kann in einen prvalue vom Typ int konvertiert werden, wenn int alle Werte von T darstellen kann;
  • andernfalls kann val in einen prvalue vom Typ unsigned int konvertiert werden.
3) In den Fällen, die in Punkt (1) (ein konvertiertes Bitfeld, das nicht in unsigned int passt) oder Punkt (2) (T ist einer der gegebenen Zeichentypen) angegeben sind, kann val in einen prvalue des ersten Typs aus der folgenden Liste konvertiert werden, der alle Werte seines zugrundeliegenden Typs darstellen kann:
  • int
  • unsigned int
  • long
  • unsigned long
  • long long
  • unsigned long long
  • der zugrundeliegende Typ von T
(seit C++11)

[bearbeiten] Promotion von Aufzählungstypen

Ein prvalue eines un-scopierten Aufzählungstyps, dessen zugrundeliegender Typ nicht festgelegt ist, kann in einen prvalue des ersten Typs aus der folgenden Liste konvertiert werden, der seinen gesamten Wertebereich aufnehmen kann:

  • int
  • unsigned int
  • long
  • unsigned long
  • sein Integer-Konvertierungsrang größer ist als der Rang von long long,
  • sein Integer-Konvertierungsrang der niedrigste aller erweiterten ganzzahligen Typen ist, und
  • er vorzeichenbehaftet ist, wenn es zwei Typen mit dem niedrigsten Integer-Konvertierungsrang unter allen erweiterten ganzzahligen Typen gibt.
(seit C++11)


Ein prvalue eines un-scopierten Aufzählungstyps, dessen zugrundeliegender Typ festgelegt ist, kann in seinen zugrundeliegenden Typ konvertiert werden. Darüber hinaus, wenn der zugrundeliegende Typ ebenfalls einer ganzzahligen Promotion unterliegt, in den beförderten zugrundeliegenden Typ. Die Konvertierung in den un-promoted zugrundeliegenden Typ ist für die Überladungsauflösung besser.

(seit C++11)

[bearbeiten] Gleitkomma-Promotion

Ein prvalue vom Typ float kann in einen prvalue vom Typ double konvertiert werden. Der Wert ändert sich nicht.

Diese Konvertierung wird als *Gleitkomma-Promotion* bezeichnet.

[bearbeiten] Numerische Konvertierungen

Im Gegensatz zu den Promotions können numerische Konvertierungen die Werte ändern, mit potenziellem Genauigkeitsverlust.

[bearbeiten] Ganzzahlige Konvertierungen

Ein prvalue eines Ganzzahltyps oder eines un-scopierten Aufzählungstyps kann in jeden anderen Ganzzahltyp konvertiert werden. Wenn die Konvertierung unter ganzzahligen Promotions aufgeführt ist, handelt es sich um eine Promotion und nicht um eine Konvertierung.

  • Wenn der Zieltyp vorzeichenlos ist, ist der resultierende Wert der kleinste vorzeichenlose Wert, der dem Quellwert modulo 2n
    entspricht, wobei n die Anzahl der Bits ist, die zur Darstellung des Zieltyps verwendet werden.
  • Das heißt, je nachdem, ob der Zieltyp breiter oder schmaler ist, werden vorzeichenbehaftete Ganzzahlen mit Vorzeichen erweitert[1] oder abgeschnitten, und vorzeichenlose Ganzzahlen werden entsprechend mit Nullen erweitert oder abgeschnitten.
  • Wenn der Zieltyp vorzeichenbehaftet ist, ändert sich der Wert nicht, wenn die Quell-Ganzzahl im Zieltyp dargestellt werden kann. Andernfalls ist das Ergebnis implementierungsabhängig(bis C++20)der eindeutige Wert des Zieltyps, der dem Quellwert modulo 2n
    entspricht, wobei n die Anzahl der Bits ist, die zur Darstellung des Zieltyps verwendet werden
    (seit C++20)
    (beachten Sie, dass dies vom Überlauf von vorzeichenbehafteter Ganzzahlarithmetik abweicht, der undefiniert ist).
  • Wenn der Quelltyp bool ist, wird der Wert false in Null und der Wert true in den Wert Eins des Zieltyps konvertiert (beachten Sie, dass dies, wenn der Zieltyp int ist, eine ganzzahlige Promotion und keine ganzzahlige Konvertierung ist).
  • Wenn der Zieltyp bool ist, handelt es sich um eine boolesche Konvertierung (siehe unten).
  1. Dies gilt nur, wenn die Arithmetik auf dem Zweierkomplement basiert, was nur für die Ganzzahltypen mit exakter Breite erforderlich ist. Beachten Sie jedoch, dass derzeit alle Plattformen mit einem C++-Compiler Zweierkomplement-Arithmetik verwenden.

[bearbeiten] Gleitkomma-Konvertierungen

Ein prvalue eines Gleitkommatyps kann in einen prvalue eines beliebigen anderen Gleitkommatyps konvertiert werden.

(bis C++23)

Ein prvalue eines Gleitkommatyps kann in einen prvalue eines beliebigen anderen Gleitkommatyps mit einem größeren oder gleichen Gleitkomma-Konvertierungsrang konvertiert werden.

Ein prvalue eines Standard-Gleitkommatyps kann in einen prvalue eines beliebigen anderen Standard-Gleitkommatyps konvertiert werden.

static_cast kann verwendet werden, um einen prvalue eines Gleitkommatyps explizit in jeden anderen Gleitkommatyp zu konvertieren.

(seit C++23)

Wenn die Konvertierung unter Gleitkomma-Promotions aufgeführt ist, handelt es sich um eine Promotion und nicht um eine Konvertierung.

  • Wenn der Quellwert exakt im Zieltyp dargestellt werden kann, ändert er sich nicht.
  • Wenn der Quellwert zwischen zwei darstellbaren Werten des Zieltyps liegt, ist das Ergebnis einer dieser beiden Werte (welcher, ist implementierungsabhängig, obwohl bei Unterstützung der IEEE-Arithmetik die Rundung standardmäßig auf die nächste Zahl erfolgt).
  • Andernfalls ist das Verhalten undefiniert.

[edit] Umwandlung von Gleitkommazahlen in Ganzzahlen

Ein prvalue vom Gleitkommatyp kann in einen prvalue eines beliebigen Ganzzahltyps umgewandelt werden. Der Bruchteil wird abgeschnitten, d.h. der Bruchteil wird verworfen.

  • Wenn der abgeschnittene Wert nicht in den Zieltyp passt, ist das Verhalten undefiniert (auch wenn der Zieltyp vorzeichenlos ist, gilt keine Modulo-Arithmetik).
  • Wenn der Zieltyp bool ist, handelt es sich um eine boolesche Umwandlung (siehe unten).

Ein prvalue vom Ganzzahltyp oder einem unbenannten Aufzählungstyp kann in einen prvalue eines beliebigen Gleitkommatyps umgewandelt werden. Das Ergebnis ist, wenn möglich, exakt.

  • Wenn der Wert in den Zieltyp passt, aber nicht exakt dargestellt werden kann, ist es implementierungsabhängig, ob der nächsthöhere oder der nächstniedrigere darstellbare Wert ausgewählt wird, obwohl bei Unterstützung der IEEE-Arithmetik die Rundung standardmäßig auf die nächste Zahl erfolgt.
  • Wenn der Wert nicht in den Zieltyp passt, ist das Verhalten undefiniert.
  • Wenn der Quelltyp bool ist, wird der Wert false in Null umgewandelt und der Wert true in Eins.

[edit] Zeigerumwandlungen

Eine Nullzeigerkonstante kann in jeden Zeigertyp umgewandelt werden, und das Ergebnis ist der Nullzeigerwert dieses Typs. Eine solche Umwandlung (bekannt als *Nullzeigerumwandlung*) darf als eine einzelne Umwandlung in einen cv-qualifizierten Typ erfolgen, d.h. sie wird nicht als Kombination aus numerischer und qualifizierender Umwandlung betrachtet.

Ein prvalue eines beliebigen (optional cv-qualifizierten) Objekttyps `T` kann in einen prvalue eines Zeigers auf (identisch cv-qualifiziertes) void umgewandelt werden. Der resultierende Zeiger repräsentiert dieselbe Speicherstelle wie der ursprüngliche Zeigerwert.

  • Wenn der ursprüngliche Zeiger ein Nullzeigerwert ist, ist das Ergebnis ebenfalls ein Nullzeigerwert des Zieltyps.

Ein prvalue `ptr` vom Typ „Zeiger auf (möglicherweise cv-qualifiziert) `Derived`“ kann in einen prvalue vom Typ „Zeiger auf (möglicherweise cv-qualifiziert) `Base`“ umgewandelt werden, wobei `Base` eine Basisklasse von `Derived` ist und `Derived` ein vollständiger Klassentyp ist. Wenn `Base` unzugänglich oder mehrdeutig ist, ist das Programm ill-formed.

  • Wenn `ptr` ein Nullzeigerwert ist, ist das Ergebnis ebenfalls ein Nullzeigerwert.
  • Andernfalls, wenn `Base` eine virtuelle Basisklasse von `Derived` ist und `ptr` nicht auf ein Objekt zeigt, dessen Typ ähnlich zu `Derived` ist und das sich innerhalb seiner Lebensdauer oder innerhalb seiner Konstruktions- oder Zerstörungsperiode befindet, ist das Verhalten undefiniert.
  • Andernfalls ist das Ergebnis ein Zeiger auf das Basisklassen-Subobjekt des abgeleiteten Klassenobjekts.

[edit] Zeiger-auf-Mitglied-Umwandlungen

Eine Nullzeigerkonstante kann in jeden Zeiger-auf-Mitglied-Typ umgewandelt werden, und das Ergebnis ist der Null-Mitgliedzeigerwert dieses Typs. Eine solche Umwandlung (bekannt als *Null-Mitgliedzeigerumwandlung*) darf als eine einzelne Umwandlung in einen cv-qualifizierten Typ erfolgen, d.h. sie wird nicht als Kombination aus numerischer und qualifizierender Umwandlung betrachtet.

Ein prvalue vom Typ „Zeiger auf Mitglied von `Base` vom Typ (möglicherweise cv-qualifiziert) `T`“ kann in einen prvalue vom Typ „Zeiger auf Mitglied von `Derived` vom Typ (identisch cv-qualifiziert) `T`“ umgewandelt werden, wobei `Base` eine Basisklasse von `Derived` ist und `Derived` ein vollständiger Klassentyp ist. Wenn `Base` unzugänglich, mehrdeutig oder eine virtuelle Basis von `Derived` ist oder eine Basis einer zwischengeschalteten virtuellen Basis von `Derived` ist, ist das Programm ill-formed.

  • Wenn `Derived` das ursprüngliche Mitglied nicht enthält und keine Basisklasse der Klasse ist, die das ursprüngliche Mitglied enthält, ist das Verhalten undefiniert.
  • Andernfalls kann der resultierende Zeiger mit einem `Derived`-Objekt dereferenziert werden, und er greift auf das Mitglied innerhalb des `Base`-Basis-Subobjekts dieses `Derived`-Objekts zu.

[edit] Boolesche Umwandlungen

Ein prvalue von Ganzzahl-, Gleitkomma-, unbenannten Aufzählungs-, Zeiger- und Zeiger-auf-Mitglied-Typen kann in einen prvalue vom Typ bool umgewandelt werden.

Der Wert Null (für Ganzzahlen, Gleitkommazahlen und unbenannte Aufzählungen) sowie die Nullzeiger- und Null-Zeiger-auf-Mitglied-Werte werden zu false. Alle anderen Werte werden zu true.

Im Kontext einer Direktinitialisierung kann ein bool-Objekt aus einem prvalue vom Typ std::nullptr_t, einschließlich nullptr, initialisiert werden. Der resultierende Wert ist false. Dies wird jedoch nicht als implizite Umwandlung betrachtet.

(seit C++11)

[edit] Qualifizierungs-Umwandlungen

Im Allgemeinen gilt:

  • Ein prvalue vom Typ Zeiger auf einen cv-qualifizierten Typ `T` kann in einen prvalue Zeiger auf denselben, stärker cv-qualifizierten Typ `T` umgewandelt werden (d.h. Konstanz und Flüchtigkeit können hinzugefügt werden).
  • Ein prvalue vom Typ Zeiger auf ein cv-qualifiziertes Mitglied vom Typ `T` in Klasse `X` kann in einen prvalue Zeiger auf ein stärker cv-qualifiziertes Mitglied vom Typ `T` in Klasse `X` umgewandelt werden.

Die formale Definition von „Qualifizierungsumwandlung“ ist unten angegeben.

[edit] Ähnliche Typen

Informell gesagt, sind zwei Typen *ähnlich*, wenn sie, abgesehen von der obersten cv-Qualifizierung,

  • derselbe Typ sind; oder
  • beides Zeiger sind und die zeigte-auf-Typen ähnlich sind; oder
  • beides Zeiger auf Mitglieder derselben Klasse sind und die Typen der zeigte-auf-Mitglieder ähnlich sind; oder
  • beides Arrays sind und die Elementtypen der Arrays ähnlich sind.

Zum Beispiel

  • const int* const * und int** sind ähnlich;
  • int (*)(int*) und int (*)(const int*) sind nicht ähnlich;
  • const int (*)(int*) und int (*)(int*) sind nicht ähnlich;
  • int (*)(int* const) und int (*)(int*) sind ähnlich (sie sind derselbe Typ);
  • std::pair<int, int> und std::pair<const int, int> sind nicht ähnlich.

Formal gesehen wird die Ähnlichkeit von Typen durch die *Qualifizierungszerlegung* definiert.

Eine *Qualifizierungszerlegung* eines Typs `T` ist eine Sequenz von Komponenten `cv_i` und `P_i`, so dass `T` als „`cv_0 P_0 cv_1 P_1 ... cv_n−1 P_n−1 cv_n U`“ für nicht-negative `n` dargestellt wird, wobei

  • jedes `cv_i` eine Menge von `const` und `volatile` ist, und
  • jedes `P_i` ist
  • „Zeiger auf“,
  • „Zeiger auf Mitglied der Klasse `C_i` vom Typ“,
  • „Array von `N_i`“, oder
  • „Array unbekannter Grenze von“.

Wenn `P_i` ein Array bezeichnet, werden die cv-Qualifizierer `cv_i+1` auf dem Elementtyp auch als cv-Qualifizierer `cv_i` des Arrays genommen.

// T is “pointer to pointer to const int”, it has 3 qualification-decompositions:
// n = 0 -> cv_0 is empty, U is “pointer to pointer to const int”
// n = 1 -> cv_0 is empty, P_0 is “pointer to”,
//          cv_1 is empty, U is “pointer to const int”
// n = 2 -> cv_0 is empty, P_0 is “pointer to”,
//          cv_1 is empty, P_1 is “pointer to”,
//          cv_2 is “const", U is “int”
using T = const int**;
 
// substitute any of the following type to U gives one of the decompositions:
// U = U0 -> the decomposition with n = 0: U0
// U = U1 -> the decomposition with n = 1: pointer to [U1]
// U = U2 -> the decomposition with n = 2: pointer to [pointer to [const U2]]
using U2 = int;
using U1 = const U2*;
using U0 = U1*;

Zwei Typen `T1` und `T2` sind *ähnlich*, wenn für jeden eine Qualifizierungszerlegung existiert, wobei alle folgenden Bedingungen für die beiden Qualifizierungszerlegungen erfüllt sind:

  • Sie haben dasselbe `n`.
  • Die von `U` bezeichneten Typen sind dieselben.
  • Die entsprechenden `P_i`-Komponenten sind dieselben oder eine ist „Array von `N_i`“ und die andere ist „Array unbekannter Grenze von“(seit C++20) für alle `i`.
// the qualification-decomposition with n = 2:
// pointer to [volatile pointer to [const int]]
using T1 = const int* volatile *;
 
// the qualification-decomposition with n = 2:
// const pointer to [pointer to [int]]
using T2 = int** const;
 
// For the two qualification-decompositions above
// although cv_0, cv_1 and cv_2 are all different,
// they have the same n, U, P_0 and P_1,
// therefore types T1 and T2 are similar.

[edit] Kombination von cv-Qualifizierungen

In der folgenden Beschreibung wird die längste Qualifizierungszerlegung des Typs `Tn` als `Dn` und ihre Komponenten als `cvn_i` und `Pn_i` bezeichnet.

Ein prvalue-Ausdruck vom Typ `T1` kann in Typ `T2` umgewandelt werden, wenn alle folgenden Bedingungen erfüllt sind:

  • `T1` und `T2` sind ähnlich.
  • Für jedes nicht-null `i`, wenn `const` in `cv1_i` enthalten ist, dann ist auch `const` in `cv2_i` enthalten, und ebenso für `volatile`.
  • Für jedes nicht-null `i`, wenn `cv1_i` und `cv2_i` unterschiedlich sind, dann ist `const` in `cv2_k` für jedes `k` in [1i) enthalten.

Der *qualifizierungskombinierte Typ* zweier Typen `T1` und `T2` ist ein Typ `T3`, der `T1` ähnlich ist und für den gilt:

  • `cv3_0` ist leer.
  • Für jedes nicht-null `i` ist `cv3_i` die Vereinigung von `cv1_i` und `cv2_i`.
  • Wenn `cv3_i` von `cv1_i` oder `c2_i` abweicht, dann wird `const` zu `cv3_k` für jedes `k` in [1i) hinzugefügt.
(bis C++20)

Der *qualifizierungskombinierte Typ* zweier Typen `T1` und `T2` ist ein Typ `T3`, der `T1` ähnlich ist, wobei `D3` alle folgenden Bedingungen erfüllt:

  • `cv3_0` ist leer.
  • Für jedes nicht-null `i` ist `cv3_i` die Vereinigung von `cv1_i` und `cv2_i`.
  • Wenn `P1_i` oder `P2_i` „Array unbekannter Grenze von“ ist, ist `P3_i` „Array unbekannter Grenze von“, andernfalls ist es `P1_i`.
  • Wenn `cv3_i` von `cv1_i` oder `cv2_i` abweicht, oder `P3_i` von `P1_i` oder `P2_i` abweicht, dann wird `const` zu `cv3_k` für jedes `k` in [1i) hinzugefügt.

Ein prvalue vom Typ `T1` kann in Typ `T2` umgewandelt werden, wenn der qualifizierungskombinierte Typ von `T1` und `T2` ein cv-unqualifiziertes `T2` ist.

(seit C++20)
// longest qualification-decomposition of T1 (n = 2):
// pointer to [pointer to [char]]
using T1 = char**;
 
// longest qualification-decomposition of T2 (n = 2):
// pointer to [pointer to [const char]]
using T2 = const char**;
 
// Determining the cv3_i and T_i components of D3 (n = 2):
// cv3_1 = empty (union of empty cv1_1 and empty cv2_1)
// cv3_2 = “const” (union of empty cv1_2 and “const” cv2_2)
// P3_0 = “pointer to” (no array of unknown bound, use P1_0)
// P3_1 = “pointer to” (no array of unknown bound, use P1_1)
// All components except cv_2 are the same, cv3_2 is different from cv1_2,
// therefore add “const” to cv3_k for each k in [1, 2): cv3_1 becomes “const”.
// T3 is “pointer to const pointer to const char”, i.e., const char* const *.
using T3 = /* the qualification-combined type of T1 and T2 */;
 
int main()
{
    const char c = 'c';
    char* pc;
    T1 ppc = &pc;
    T2 pcc = ppc; // Error: T3 is not the same as cv-unqualified T2,
                  //        no implicit conversion.
 
    *pcc = &c;
    *pc = 'C';    // If the erroneous assignment above is allowed,
                  // the const object “c” may be modified.
}

Beachten Sie, dass in der Programmiersprache C `const`/`volatile` nur auf der ersten Ebene hinzugefügt werden können.

char** p = 0;
char * const* p1 = p;       // OK in C and C++
const char* const * p2 = p; // error in C, OK in C++

Funktionszeigerumwandlungen

  • Ein prvalue vom Typ Zeiger auf eine nicht-werfende Funktion kann in einen prvalue Zeiger auf eine potenziell werfende Funktion umgewandelt werden.
  • Ein prvalue vom Typ Zeiger auf eine nicht-werfende Memberfunktion kann in einen prvalue Zeiger auf eine potenziell werfende Memberfunktion umgewandelt werden.
void (*p)();
void (**pp)() noexcept = &p; // error: cannot convert to pointer to noexcept function
 
struct S
{
    typedef void (*p)();
    operator p();
};
void (*q)() noexcept = S(); // error: cannot convert to pointer to noexcept function
(seit C++17)

[edit] Das Safe-Bool-Problem

Bis C++11 stellte die Gestaltung einer Klasse, die in booleschen Kontexten verwendet werden sollte (z.B. `if (obj) { ... }`), ein Problem dar: Angenommen, eine benutzerdefinierte Konvertierungsfunktion wie `T::operator bool() const;`, erlaubte die implizite Konvertierungssequenz eine zusätzliche Standardkonvertierungssequenz nach diesem Funktionsaufruf. Das bedeutet, dass das resultierende `bool` in `int` umgewandelt werden konnte, was Code wie `obj << 1;` oder `int i = obj;` ermöglichte.

Eine frühe Lösung hierfür findet sich in `std::basic_ios`, das ursprünglich `operator void*` definierte, sodass Code wie `if (std::cin) { ... }` kompiliert, weil `void*` in `bool` umgewandelt werden kann, aber `int n = std::cout;` nicht kompiliert, da `void*` nicht in `int` umgewandelt werden kann. Dies erlaubt immer noch unsinnigen Code wie `delete std::cout;`.

Viele Third-Party-Bibliotheken vor C++11 wurden mit einer ausgefeilteren Lösung entworfen, dem sogenannten Safe Bool Idiom. `std::basic_ios` ermöglichte dieses Idiom auch über LWG-Issue 468, und `operator void*` wurde ersetzt (siehe Notizen).

Seit C++11 kann die explizite boolesche Umwandlung ebenfalls zur Lösung des Safe-Bool-Problems verwendet werden.

[edit] Fehlerberichte

Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.

DR angewendet auf Verhalten wie veröffentlicht Korrigiertes Verhalten
CWG 170 C++98 Das Verhalten von Zeiger-auf-Mitglied-Umwandlungen war unklar.
Wenn die abgeleitete Klasse nicht das ursprüngliche Mitglied hat.
wurde klargestellt
CWG 172 C++98 Der Aufzählungstyp wurde basierend auf seinem zugrunde liegenden Typ befördert. Stattdessen basierend auf seinem Wertebereich.
CWG 330
(N4261)
C++98 Die Umwandlung von `double* const ( *p )[3]`
zu `double const * const ( *p )[3]` war ungültig.
Wurde gültig gemacht.
CWG 519 C++98 Nullzeigerwerte blieben nicht garantiert erhalten.
Beim Umwandeln in einen anderen Zeigertyp.
Immer erhalten.
CWG 616 C++98 Das Verhalten der lvalue-zu-rvalue-Umwandlung von
jedem nicht initialisierten Objekt und Zeigerobjekten
mit ungültigen Werten war immer undefiniert.
Indeterminiertes `unsigned char`
ist erlaubt; die Verwendung von ungültigen Zeigern
ist implementierungsabhängig.
CWG 685 C++98 Der zugrunde liegende Typ eines Aufzählungstyps wurde
nicht im Integral-Promotion priorisiert, wenn er festgelegt ist.
Priorisiert.
CWG 707 C++98 Umwandlung von Ganzzahl zu Gleitkommazahl
hatte in allen Fällen definiertes Verhalten.
Das Verhalten ist undefiniert, wenn
der umzuwandelnde Wert
außerhalb des Zielbereichs liegt.
CWG 1423 C++11 `std::nullptr_t` war in bool` umwandelbar
sowohl in der Direkt- als auch in der Kopierinitialisierung.
Nur in der Direktinitialisierung.
CWG 1773 C++11 Ein Name-Ausdruck, der in einem potenziell ausgewerteten
Ausdruck vorkommt, so dass das benannte Objekt nicht odr-used ist, könnte
während einer lvalue-zu-rvalue-Umwandlung immer noch ausgewertet werden.
Nicht ausgewertet.
CWG 1781 C++11 `std::nullptr_t` zu `bool` wurde als implizit
betrachtet, obwohl es nur für die Direktinitialisierung gültig ist.
Nicht mehr als
implizite Umwandlung betrachtet.
CWG 1787 C++98 Das Verhalten beim Lesen von einem indeterminate
unsigned char, der in einem Register zwischengespeichert wurde, war undefiniert.
wurde wohlformuliert
CWG 1981 C++11 Kontextuelle Umwandlungen berücksichtigten explizite Konvertierungsfunktionen. nicht betrachtet
CWG 2140 C++11 Es war unklar, ob lvalue-zu-rvalue-Umwandlungen von
std::nullptr_t lvalues diese lvalues aus dem Speicher abrufen.
Nicht abgerufen.
CWG 2310 C++98 Für Zeigerumwandlungen von abgeleitet zu Basis und
Zeiger-auf-Mitglied-Umwandlungen von Basis zu abgeleitet
konnte der abgeleitete Klassentyp unvollständig sein.
Muss vollständig sein.
CWG 2484 C++20 char8_t und char16_t hatten unterschiedliche Integral
Beförderungsstrategien, aber sie passen beide.
char8_t sollte befördert werden
auf die gleiche Weise wie char16_t.
CWG 2485 C++98 Integral-Beförderungen, die Bitfelder betrafen, waren nicht gut spezifiziert. Verbesserte die Spezifikation.
CWG 2813 C++23 Bei der Invokation einer expliziten
Objekt-Member-Funktion eines Klassen-prvalues wurde eine temporäre Materialisierung durchgeführt.
Wird nicht auftreten.
in diesem Fall
CWG 2861 C++98 Ein Zeiger auf ein typ-nicht erreichbares Objekt konnte
in einen Zeiger auf ein Basisklassen-Subobjekt umgewandelt werden.
Das Verhalten ist
in diesem Fall nicht definiert.
CWG 2879 C++17 Die temporäre Materialisierungskonvertierung wurde auf prvalues angewendet
als Operand eines Operators, der ein glvalue erwartet.
In einigen Fällen nicht angewendet.
CWG 2899 C++98 lvalue-zu-rvalue-Konvertierungen konnten auf lvalues angewendet werden
die Objekte mit ungültigen Wertdarstellungen bezeichnen.
Das Verhalten ist
in diesem Fall nicht definiert.
CWG 2901 C++98 Das Ergebnis der lvalue-zu-rvalue-Konvertierung eines
unsigned int lvalues, das auf ein `int`-Objekt mit dem Wert `-1` verweist, war unklar.
wurde klargestellt

[edit] Siehe auch

C-Dokumentation für Implizite Umwandlungen