Namespaces
Variants

Implicit conversions

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Implicit conversions
static_cast
const_cast
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Implizite Konvertierungen werden durchgeführt, wenn ein Ausdruck eines 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;
  • beim Initialisieren eines neuen Objekts vom Typ T2 , einschließlich return -Anweisung in einer Funktion, die T2 zurückgibt;
  • wenn der Ausdruck in einer switch -Anweisung verwendet wird ( T2 ist integraler Typ);
  • wenn der Ausdruck in einer if -Anweisung oder einer Schleife verwendet wird ( T2 ist bool ).

Das Programm ist wohlgeformt (kompiliert) nur wenn es eine eindeutige implicit conversion sequence von T1 zu T2 gibt.

Wenn es mehrere Überladungen der aufgerufenen Funktion oder des Operators gibt, wird nach dem Aufbau der impliziten Konvertierungssequenz von T1 zu jedem verfügbaren T2 durch die Regeln der Überladungsauflösung entschieden, welche Überladung kompiliert wird.

Hinweis: In arithmetischen Ausdrücken wird der Zieltyp für die impliziten Konvertierungen der Operanden binärer Operatoren durch einen separaten Regelsatz bestimmt: usual arithmetic conversions .

Inhaltsverzeichnis

Reihenfolge der Konvertierungen

Die implizite Konvertierungssequenz besteht aus den folgenden Schritten in dieser Reihenfolge:

1) null oder ein standard conversion sequence ;
2) null oder eine benutzerdefinierte Konvertierung ;
3) null oder eine standard conversion sequence (nur wenn eine benutzerdefinierte Konvertierung verwendet wird).

Bei der Betrachtung des Arguments für einen Konstruktor oder eine benutzerdefinierte Konvertierungsfunktion ist nur eine Standardkonvertierungssequenz zulässig (andernfalls könnten benutzerdefinierte Konvertierungen effektiv verkettet werden). Bei der Konvertierung von einem Nicht-Klassentyp zu einem anderen Nicht-Klassentyp ist nur eine Standardkonvertierungssequenz zulässig.

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

1) null oder eine Konvertierung aus der folgenden Menge:
  • lvalue-to-rvalue conversion ,
  • array-to-pointer conversion , und
  • function-to-pointer conversion ;
2) null oder eine numerische Höherstufung oder numerische Konvertierung ;
3) null oder eine Funktionszeigerkonvertierung ;
(since C++17)
4) null oder eine qualification conversion .

Eine benutzerdefinierte Konvertierung besteht aus null oder einem nicht-expliziten Einzelargument- converting constructor oder nicht-expliziten conversion function Aufruf.

Ein Ausdruck e wird als implizit konvertierbar zu T2 bezeichnet genau dann, wenn T2 durch Copy-Initialisierung aus e initialisiert werden kann, das heißt die Deklaration T2 t = e ; wohlgeformt ist (kompiliert werden kann), für eine erfundene temporäre Variable t . Beachten Sie, dass dies sich von Direktinitialisierung ( T2 t ( e ) ) unterscheidet, bei der zusätzlich explizite Konstruktoren und Konvertierungsfunktionen berücksichtigt werden.

Kontextuelle Konvertierungen

In den folgenden Kontexten wird der Typ bool erwartet und die implizite Konvertierung wird durchgeführt, falls die Deklaration bool t ( e ) ; wohlgeformt ist (das heißt, eine explizite Konvertierungsfunktion wie explicit T :: operator bool ( ) const ; wird berücksichtigt). Ein solcher Ausdruck e wird als kontextuell zu 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 einer noexcept -Spezifikation;
(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 zulässig, wenn

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

Ein solcher Ausdruck e wird als kontextuell implizit konvertiert zum angegebenen Typ T bezeichnet. Beachten Sie, dass explizite Konvertierungsfunktionen nicht berücksichtigt werden, obwohl sie bei kontextuellen Konvertierungen zu bool berücksichtigt werden. (seit C++11)

  • das Argument des delete-Ausdrucks ( T ist ein beliebiger Objektzeigertyp);
  • integrale konstante Ausdrücke , bei denen eine Literalklasse verwendet wird ( T ist ein beliebiger integraler oder unbegrenzter Aufzählungstyp, die ausgewählte benutzerdefinierte Konvertierungsfunktion muss constexpr sein);
  • der kontrollierende Ausdruck der switch -Anweisung ( T ist ein beliebiger integraler 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) {}     // Fehler bis C++14 (mehr als eine Konvertierungsfunktion)
                      // OK ab C++14 (beide Funktionen konvertieren zum selben Typ int)
    switch (i + 0) {} // immer korrekt (implizite Konvertierung)
}

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 einen Prvalue für diesen Operanden erfordert, werden die Lvalue-zu-Rvalue -, Array-zu-Zeiger - oder Funktion-zu-Zeiger -Standardkonvertierungen angewendet, um den Ausdruck in einen Prvalue umzuwandeln.
  • Sofern nicht anders angegeben, wird immer dann, wenn ein Prvalue als Operand eines Operators erscheint, der einen Glvalue für diesen Operanden erwartet, die temporary materialization conversion angewendet, um den Ausdruck in einen Xvalue umzuwandeln.
(since C++17)

Lvalue-zu-Rvalue-Konvertierung

Ein Lvalue (bis C++11) Ein Glvalue (seit C++11) eines beliebigen Nicht-Funktions-, Nicht-Array-Typs T kann implizit konvertiert werden in einen Rvalue (bis C++11) einen Prvalue (seit C++11) :

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

Wenn eine Lvalue-zu-Rvalue-Konvertierung von einem unvollständigen Typ durch ein Programm erforderlich ist, ist dieses Programm fehlerhaft.

Gegeben das Objekt, auf das die lvalue (until C++11) glvalue (since C++11) referenziert als obj :

  • Das Ergebnis der Konvertierung ist der in obj enthaltene Wert. Wenn einer von T und dem Typ von obj ein vorzeichenbehafteter Ganzzahltyp ist und der andere der entsprechende vorzeichenlose Ganzzahltyp, ist das Ergebnis der Wert vom Typ T mit derselben Wertdarstellung wie obj .
(bis C++11)
  • Wenn eine Lvalue-zu-Rvalue-Konvertierung auf einen Ausdruck E angewendet wird, wird auf den in obj enthaltenen Wert nicht zugegriffen, 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 durch die Konvertierung nicht zugegriffen, daher gibt es keine Seiteneffekte, selbst wenn T volatile-qualifiziert ist, und der Glvalue kann auf ein inaktives Mitglied einer Union verweisen.
  • Andernfalls, wenn T ein Klassentyp ist:
(bis C++17)
(seit C++17)
  • Andernfalls, wenn obj einen ungültigen Zeigerwert enthält, ist das Verhalten implementierungsdefiniert.
  • Andernfalls, wenn die Bits in der Wertdarstellung von obj nicht gültig für den Typ von obj sind, ist das Verhalten undefiniert.
  • Andernfalls, obj wird gelesen, und (seit C++20) das Ergebnis ist der in obj enthaltene Wert. Wenn einer von T und dem Typ von obj ein vorzeichenbehafteter Ganzzahltyp ist und der andere der entsprechende vorzeichenlose Ganzzahltyp, ist das Ergebnis der Wert vom Typ T mit derselben Wertdarstellung wie obj .
(seit C++11)

Diese Konvertierung modelliert den Vorgang des Lesens eines Wertes aus einer Speicheradresse in ein CPU-Register.

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, erfolgt temporäre Materialisierung . (seit C++17) Der resultierende Zeiger verweist auf das erste Element des Arrays (siehe Array-zu-Zeiger-Zerfall für Details).

Function-to-Pointer-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 sein Ergebnisobjekt ausgewertet wird, und erzeugt einen Xvalue, der das temporäre Objekt bezeichnet.

Wenn T ein Klassen- oder Array von Klassentyp ist, muss es 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 temporäre Materialisierung nicht auftritt, wenn ein Objekt aus einem Prvalue desselben Typs initialisiert wird (durch Direktinitialisierung oder Kopierinitialisierung ): Ein solches Objekt wird direkt vom Initialisierer initialisiert. Dies gewährleistet "garantierte Kopierelision".

(seit C++17)

Ganzzahlige Höherstufung

prvalues von kleinen Ganzzahltypen (wie char ) und Aufzählungstypen ohne Gültigkeitsbereich können in prvalues von größeren Ganzzahltypen (wie int ) konvertiert werden. Insbesondere akzeptieren arithmetische Operatoren keine Typen kleiner als int als Argumente, und Ganzzahlaufwertungen werden automatisch nach der Lvalue-zu-Rvalue-Konvertierung angewendet, falls zutreffend. Diese Konvertierung bewahrt stets den Wert.

Die folgenden impliziten Konvertierungen in diesem Abschnitt werden als integral promotions klassifiziert.

Beachten Sie, dass für einen gegebenen Quelltyp der Zieltyp der integralen Promotion eindeutig ist, und alle anderen Konvertierungen keine Promotionen sind. Zum Beispiel wählt overload resolution char -> int (Promotion) gegenüber char -> short (Konvertierung).

Höherstufung 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 auf ein Bitfeld angewendet ist,
  • val kann in einen Prvalue des Typs int konvertiert werden, falls int alle Werte des Bitfelds darstellen kann;
  • andernfalls kann val in unsigned int konvertiert werden, falls unsigned int alle Werte des Bitfelds darstellen kann;
  • andernfalls kann val gemäß den in Punkt (3) spezifizierten Regeln konvertiert werden.
2) Andernfalls ( val wird nicht von einem Bitfeld konvertiert),
  • wenn T char8_t , (seit C++20) char16_t , char32_t oder (seit C++11) wchar_t ist, val kann gemäß den in Punkt (3) spezifizierten Regeln konvertiert werden;
  • andernfalls, wenn der Integer-Konversionsrang von T niedriger ist als der Rang von int :
  • val kann in einen Prvalue des Typs int konvertiert werden, wenn int alle Werte von T darstellen kann;
  • andernfalls kann val in einen Prvalue des Typs unsigned int konvertiert werden.
3) In den durch Punkt (1) (ein konvertiertes Bitfeld, das nicht in unsigned int passt) oder Punkt (2) ( T ist einer der angegebenen Zeichentypen) spezifizierten Fällen kann val zu einem PR-Wert des ersten der folgenden Typen 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)

Höherstufung von Aufzählungstypen

Ein prvalue eines unbegrenzten enumeration -Typs, dessen zugrundeliegender Typ nicht festgelegt ist, kann in einen prvalue des ersten Typs aus der folgenden Liste konvertiert werden, der ihren gesamten Wertebereich aufnehmen kann:

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


Ein Prvalue eines unbegrenzten Aufzählungstyps mit festgelegtem zugrundeliegendem Typ kann in seinen zugrundeliegenden Typ konvertiert werden. Wenn der zugrundeliegende Typ außerdem einer Ganzzahlpromotion unterliegt, kann die Konvertierung zum promovierten zugrundeliegenden Typ erfolgen. Für die Zwecke der Überladungsauflösung ist die Konvertierung zum nicht-promovierten zugrundeliegenden Typ besser.

(seit C++11)

Gleitkomma-Promotion

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

Diese Konvertierung wird als floating-point promotion bezeichnet.

Numerische Konvertierungen

Im Gegensatz zu den Promotions können numerische Konvertierungen die Werte ändern, mit möglichem Präzisionsverlust.

Ganzzahlige Konvertierungen

Ein prvalue eines ganzzahligen Typs oder eines unbegrenzten Aufzählungstyps kann in jeden anderen ganzzahligen Typ konvertiert werden. Falls die Konversion unter Ganzzahl-Höherstufungen aufgeführt ist, handelt es sich um eine Höherstufung und nicht um eine Konversion.

  • Wenn der Zieltyp vorzeichenlos ist, ist der resultierende Wert der kleinste vorzeichenlose Wert, der dem Quellwert modulo 2 n
    entspricht, wobei n die Anzahl der Bits zur Darstellung des Zieltyps ist.
  • Das heißt, abhängig davon, ob der Zieltyp breiter oder schmaler ist, werden vorzeichenbehaftete Ganzzahlen sign-erweitert [1] oder abgeschnitten und vorzeichenlose Ganzzahlen werden null-erweitert oder entsprechend abgeschnitten.
  • Wenn der Zieltyp vorzeichenbehaftet ist, ändert sich der Wert nicht, wenn die Quell-Ganzzahl im Zieltyp dargestellt werden kann. Andernfalls ist das Ergebnis implementierungsdefiniert (bis C++20) der eindeutige Wert des Zieltyps, der gleich dem Quellwert modulo 2 n
    ist, wobei n die Anzahl der Bits zur Darstellung des Zieltyps ist
    (seit C++20)
    (beachten Sie, dass dies sich von vorzeichenbehaftetem Ganzzahl-Überlauf unterscheidet, der undefiniert ist).
  • Wenn der Quelltyp bool ist, wird der Wert false in null konvertiert und der Wert true in den Wert eins des Zieltyps (beachten Sie, dass dies bei Zieltyp int eine Ganzzahl-Höherwertung, keine Ganzzahl-Konvertierung ist).
  • Wenn der Zieltyp bool ist, handelt es sich um eine boolesche Konvertierung (siehe unten).
  1. Dies gilt nur, wenn die Arithmetik im Zweierkomplement erfolgt, was nur für die exact-width integer types erforderlich ist. Beachten Sie jedoch, dass derzeit alle Plattformen mit einem C++-Compiler Zweierkomplement-Arithmetik verwenden.

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 standardmäßigen Gleitkommatyps kann in einen prvalue eines beliebigen anderen standardmäßigen Gleitkommatyps konvertiert werden.

static_cast kann verwendet werden, um einen prvalue eines Gleitkommatyps explizit in einen beliebigen 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 (implementierungsdefiniert welcher, obwohl bei Unterstützung von IEEE-Arithmetik die Standardrundung to nearest ist).
  • Andernfalls ist das Verhalten undefiniert.

Gleitkomma-Ganzzahl-Konvertierungen

Ein prvalue vom Gleitkommatyp kann in einen prvalue eines beliebigen Ganzzahltyps konvertiert werden. Der Bruchteil wird abgeschnitten, das heißt, der Bruchteil wird verworfen.

  • Wenn der abgeschnittene Wert nicht in den Zieltyp passt, ist das Verhalten undefiniert (selbst wenn der Zieltyp vorzeichenlos ist, kommt Modulo-Arithmetik nicht zur Anwendung).
  • Wenn der Zieltyp bool ist, handelt es sich um eine boolesche Konvertierung (siehe unten ).

Ein prvalue vom Typ Integer oder unbegrenzter Aufzählungstyp kann in einen prvalue eines beliebigen Gleitkommatyps konvertiert werden. Das Ergebnis ist exakt, wenn möglich.

  • Wenn der Wert in den Zieltyp passt, aber nicht exakt dargestellt werden kann, ist implementierungsdefiniert, ob der nächsthöhere oder nächstniedrigere darstellbare Wert gewählt wird, obwohl bei Unterstützung von IEEE-Arithmetik die Rundung standardmäßig auf den nächsten Wert erfolgt.
  • Wenn der Wert nicht in den Zieltyp passt, ist das Verhalten undefiniert.
  • Wenn der Quelltyp bool ist, wird der Wert false in null und der Wert true in eins konvertiert.

Zeigerkonvertierungen

Ein null pointer constant kann in jeden Zeigertyp konvertiert werden, und das Ergebnis ist der Nullzeigerwert dieses Typs. Eine solche Konvertierung (bekannt als null pointer conversion ) ist als einzelne Konvertierung in einen cv-qualifizierten Typ erlaubt, das heißt, sie wird nicht als Kombination numerischer und qualifizierender Konvertierungen betrachtet.

Ein prvalue -Zeiger auf einen beliebigen (optional cv-qualifizierten) Objekttyp T kann in einen prvalue-Zeiger auf (identisch cv-qualifiziertes) void konvertiert werden. Der resultierende Zeiger repräsentiert denselben Speicherort wie der ursprüngliche Zeigerwert.

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

Ein Prvalue ptr vom Typ "Zeiger auf (möglicherweise cv-qualifiziertes) Derived " kann in einen Prvalue vom Typ "Zeiger auf (möglicherweise cv-qualifiziertes) Base " konvertiert 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 fehlerhaft.

  • 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 Destruktionsphase befindet, ist das Verhalten undefiniert.
  • Andernfalls ist das Ergebnis ein Zeiger auf das Basisklassen-Subobjekt des abgeleiteten Klassenobjekts.

Pointer-zu-Member-Konvertierungen

Ein null pointer constant kann in jeden Zeiger-auf-Mitglied-Typ konvertiert werden, und das Ergebnis ist der Null-Mitglied-Zeiger-Wert dieses Typs. Eine solche Konvertierung (bekannt als Null-Mitglied-Zeiger-Konvertierung ) ist erlaubt, um in einen cv-qualifizierten Typ als einzelne Konvertierung umzuwandeln, das heißt, sie wird nicht als Kombination von numerischen und Qualifizierungskonvertierungen 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 " konvertiert werden, wobei Base eine Basisklasse von Derived ist und Derived ein vollständiger Klassentyp ist. Wenn Base eine unzugängliche, mehrdeutige oder virtuelle Basis von Derived ist oder eine Basis einer intermediären virtuellen Basis von Derived ist, ist das Programm fehlerhaft.

  • Wenn Derived das ursprüngliche Member nicht enthält und keine Basisklasse der Klasse ist, die das ursprüngliche Member enthält, ist das Verhalten undefiniert.
  • Andernfalls kann der resultierende Zeiger mit einem Derived -Objekt dereferenziert werden, und er wird auf das Member innerhalb des Base -Basisunterobjekts dieses Derived -Objekts zugreifen.

Boolesche Konvertierungen

Ein prvalue von integralen, Gleitkomma-, unbegrenzten Aufzählungstypen, Zeiger- und Zeiger-auf-Mitglieder-Typen kann in einen prvalue vom Typ bool konvertiert werden.

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

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

(seit C++11)

Qualifikationskonvertierungen

Allgemein gesprochen:

  • Ein prvalue vom Typ Zeiger auf cv-qualifizierten Typ T kann in einen prvalue-Zeiger auf einen stärker cv-qualifizierten gleichen Typ T konvertiert werden (mit anderen Worten: Constness und Volatilität können hinzugefügt werden).
  • Ein prvalue vom Typ Zeiger auf Mitglied eines cv-qualifizierten Typs T in Klasse X kann in einen prvalue-Zeiger auf Mitglied eines stärker cv-qualifizierten Typs T in Klasse X konvertiert werden.

Die formale Definition von "qualification conversion" wird unten gegeben.

Ähnliche Typen

Informell sind zwei Typen ähnlich wenn, unter Vernachlässigung der Top-Level-cv-Qualifikation:

  • sie sind vom gleichen Typ; oder
  • sie sind beide Zeiger und die gezeigten Typen sind ähnlich; oder
  • sie sind beide Zeiger auf Mitglieder derselben Klasse, und die Typen der gezeigten Mitglieder sind ähnlich; oder
  • sie sind beide Arrays und die Array-Elementtypen sind ähnlich.

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 wird Typähnlichkeit in Bezug auf Qualifikationszerlegung definiert.

Eine Qualifikationszerlegung eines Typs T ist eine Sequenz von Komponenten cv_i und P_i , sodass T cv_0 P_0 cv_1 P_1 ... cv_n−1 P_n−1 cv_n U “ für nicht-negative n ist, wobei

  • jedes cv_i ist eine Kombination aus const und volatile , und
  • jedes P_i ist
  • "Zeiger auf",
  • "Zeiger auf Element der Klasse C_i vom Typ",
  • "Array von N_i ", oder
  • "Array unbekannter Größe von".

Wenn P_i ein Array bezeichnet, werden die cv-Qualifizierer cv_i+1 des Elementtyps auch als die cv-Qualifizierer cv_i des Arrays übernommen.

// T ist "Zeiger auf Zeiger auf const int", es hat 3 Qualifikations-Zerlegungen:
// n = 0 -> cv_0 ist leer, U ist "Zeiger auf Zeiger auf const int"
// n = 1 -> cv_0 ist leer, P_0 ist "Zeiger auf",
//          cv_1 ist leer, U ist "Zeiger auf const int"
// n = 2 -> cv_0 ist leer, P_0 ist "Zeiger auf",
//          cv_1 ist leer, P_1 ist "Zeiger auf",
//          cv_2 ist "const", U ist "int"
using T = const int**;
// Ersetzen von U mit einem der folgenden Typen ergibt eine der Zerlegungen:
// U = U0 -> die Zerlegung mit n = 0: U0
// U = U1 -> die Zerlegung mit n = 1: Zeiger auf [U1]
// U = U2 -> die Zerlegung mit n = 2: Zeiger auf [Zeiger auf [const U2]]
using U2 = int;
using U1 = const U2*;
using U0 = U1*;

Zwei Typen T1 und T2 sind ähnlich , wenn für jeden von ihnen eine Qualifikationszerlegung existiert, wobei für die beiden Qualifikationszerlegungen alle folgenden Bedingungen erfüllt sind:

  • Sie haben dasselbe n .
  • Die durch U bezeichneten Typen sind identisch.
  • Die entsprechenden P_i Komponenten sind identisch oder eine ist "Array von N_i " und die andere ist "Array unbekannter Größe von" (seit C++20) für alle i .
// die Qualifikationszerlegung mit n = 2:
// Zeiger auf [flüchtigen Zeiger auf [const int]]
using T1 = const int* volatile *;
// die Qualifikationszerlegung mit n = 2:
// konstanter Zeiger auf [Zeiger auf [int]]
using T2 = int** const;
// Für die beiden obigen Qualifikationszerlegungen
// obwohl cv_0, cv_1 und cv_2 alle unterschiedlich sind,
// haben sie dasselbe n, U, P_0 und P_1,
// daher sind die Typen T1 und T2 ähnlich.

Kombinieren von cv-Qualifizierern

In der folgenden Beschreibung bezeichnet die längste Qualifikationszerlegung des Typs Tn als Dn , und ihre Komponenten werden als cvn_i und Pn_i bezeichnet.

Ein prvalue-Ausdruck vom Typ T1 kann in den Typ T2 konvertiert 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 const auch in cv2_i enthalten, und analog 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 [ 1 , i ) enthalten.

Der qualifikationskombinierte Typ zweier Typen T1 und T2 ist ein Typ T3 , der ähnlich zu T1 ist, sodass

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

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

  • cv3_0 ist leer.
  • Für jedes nicht-null i , cv3_i die Vereinigung von cv1_i und cv2_i ist.
  • Wenn P1_i oder P2_i "array of unknown bound of" ist, dann ist P3_i "array of unknown bound of", andernfalls ist es P1_i .
  • Wenn cv3_i sich von cv1_i oder cv2_i unterscheidet, oder P3_i sich von P1_i oder P2_i unterscheidet, dann wird const zu cv3_k für jedes k in [ 1 , i ) hinzugefügt.

Ein prvalue vom Typ T1 kann in den Typ T2 konvertiert werden, wenn der qualifikationskombinierte Typ von T1 und T2 cv-unqualifiziertes T2 ist.

(seit C++20)
// längste Qualifikationszerlegung von T1 (n = 2):
// Zeiger auf [Zeiger auf [char]]
using T1 = char**;
// längste Qualifikationszerlegung von T2 (n = 2):
// Zeiger auf [Zeiger auf [const char]]
using T2 = const char**;
// Bestimmung der cv3_i und T_i Komponenten von D3 (n = 2):
// cv3_1 = leer (Vereinigung von leer cv1_1 und leer cv2_1)
// cv3_2 = "const" (Vereinigung von leer cv1_2 und "const" cv2_2)
// P3_0 = "Zeiger auf" (kein Array unbekannter Größe, verwende P1_0)
// P3_1 = "Zeiger auf" (kein Array unbekannter Größe, verwende P1_1)
// Alle Komponenten außer cv_2 sind gleich, cv3_2 unterscheidet sich von cv1_2,
// daher füge "const" zu cv3_k für jedes k in [1, 2) hinzu: cv3_1 wird "const".
// T3 ist "Zeiger auf const Zeiger auf const char", d.h. const char* const *.
using T3 = /* der qualifikationskombinierte Typ von T1 und T2 */;
int main()
{
    const char c = 'c';
    char* pc;
    T1 ppc = &pc;
    T2 pcc = ppc; // Fehler: T3 ist nicht identisch mit cv-unqualifiziertem T2,
                  //        keine implizite Konvertierung.
    *pcc = &c;
    *pc = 'C';    // Wenn die fehlerhafte Zuweisung oben erlaubt wäre,
                  // könnte das const-Objekt "c" modifiziert werden.
}

Beachten Sie, dass in der Programmiersprache C const / volatile nur auf der ersten Ebene hinzugefügt werden kann:

char** p = 0;
char * const* p1 = p;       // OK in C und C++
const char* const * p2 = p; // Fehler in C, OK in C++

Funktionszeiger-Konvertierungen

  • Ein Prvalue vom Typ Zeiger auf nicht-werfende Funktion kann in einen Prvalue-Zeiger auf potenziell werfende Funktion konvertiert werden.
  • Ein Prvalue vom Typ Zeiger auf nicht-werfende Memberfunktion kann in einen Prvalue-Zeiger auf potenziell werfende Memberfunktion konvertiert 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)

Das Safe-Bool-Problem

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

Eine frühe Lösung dafür ist in std::basic_ios zu sehen, das ursprünglich operator void * definiert, sodass Code wie if ( std:: cin ) { ... } kompiliert, weil void * in bool konvertierbar ist, aber int n = std:: cout ; nicht kompiliert, da void * nicht in int konvertierbar ist. Dies erlaubt jedoch weiterhin unsinnigen Code wie delete std:: cout ; zu kompilieren.

Viele Drittanbieterbibliotheken vor C++11 wurden mit einer aufwendigeren Lösung entworfen, bekannt als das Safe Bool Idiom . std::basic_ios erlaubte dieses Idiom ebenfalls über LWG issue 468 , und operator void * wurde ersetzt (siehe Anmerkungen ).

Seit C++11 kann auch die explizite bool-Konvertierung verwendet werden, um das Safe-Bool-Problem zu lösen.

Fehlerberichte

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

DR Angewendet auf Veröffentlichtes Verhalten Korrektes Verhalten
CWG 170 C++98 Das Verhalten von Pointer-to-Member-Konvertierungen war unklar
wenn die abgeleitete Klasse das ursprüngliche Member nicht besitzt
klargestellt
CWG 172 C++98 Aufzählungstyp wurde basierend auf seinem zugrunde liegenden Typ heraufgestuft basierend auf seinem Wertebereich stattdessen
CWG 330
( N4261 )
C++98 die Konvertierung von double * const ( * p ) [ 3 ]
zu double const * const ( * p ) [ 3 ] war ungültig
gültig gemacht
CWG 519 C++98 Nullzeigerwerte waren nicht garantiert
bei Konvertierung in einen anderen Zeigertyp erhalten
immer erhalten
CWG 616 C++98 das Verhalten der Lvalue-zu-Rvalue-Konvertierung von
beliebigen nicht initialisierten Objekten und Zeigerobjekten
mit ungültigen Werten war stets undefiniert
unbestimmter unsigned char
ist erlaubt; Verwendung ungültiger Zeiger
ist implementierungsdefiniert
CWG 685 C++98 der zugrundeliegende Typ eines Aufzählungstyps wurde
bei der Ganzzahl-Höherstufung nicht priorisiert, wenn er festgelegt ist
priorisiert
CWG 707 C++98 Ganzzahl-zu-Gleitkomma-Konvertierung
hatte in allen Fällen definiertes Verhalten
das Verhalten ist undefiniert, wenn
der zu konvertierende Wert
außerhalb des Zielbereichs liegt
CWG 1423 C++11 std::nullptr_t war konvertierbar zu bool
sowohl in Direkt- als auch Kopierinitialisierung
nur Direktinitialisierung
CWG 1773 C++11 Ein Namensausdruck, der in einem potenziell ausgewerteten
Ausdruck erscheint, sodass das benannte Objekt nicht odr-used ist, könnte
dennoch während einer Lvalue-zu-Rvalue-Konvertierung ausgewertet werden
nicht ausgewertet
CWG 1781 C++11 std::nullptr_t zu bool wurde als implizite
Konvertierung betrachtet, obwohl sie nur für die Direktinitialisierung gültig ist
wird nicht mehr als
implizite Konvertierung betrachtet
CWG 1787 C++98 Das Verhalten beim Lesen von einem undefinierten
unsigned char , der in einem Register zwischengespeichert war, war undefiniert
wurde wohldefiniert gemacht
CWG 1981 C++11 Kontextuelle Konvertierungen betrachteten explizite Konvertierungsfunktionen nicht berücksichtigt
CWG 2140 C++11 Es war unklar, ob Lvalue-zu-Rvalue-Konvertierungen von
std::nullptr_t Lvalues diese Lvalues aus dem Speicher abrufen
nicht abgerufen
CWG 2310 C++98 für abgeleitete-zu-Basis-Zeigerkonvertierungen und
Basis-zu-abgeleitet Zeiger-auf-Mitglied-Konvertierungen,
konnte der abgeleitete Klassentyp unvollständig sein
muss vollständig sein
CWG 2484 C++20 char8_t und char16_t hatten unterschiedliche Ganzzahl-
Höherstufungsstrategien, aber sie können beide aufnehmen
char8_t sollte auf die gleiche Weise
heraufgestuft werden wie char16_t
CWG 2485 C++98 Ganzzahlpromotions mit Bitfeldern waren nicht gut spezifiziert Spezifikation verbessert
CWG 2813 C++23 Temporäres Materialisieren würde auftreten, wenn eine explizite
Objekt-Memberfunktion eines Klassen-Prvalue aufgerufen wird
tritt in diesem Fall
nicht auf
CWG 2861 C++98 ein Zeiger auf ein typunzugängliches Objekt könnte
in einen Zeiger auf ein Basisklassen-Subobjekt konvertiert werden
das Verhalten ist
in diesem Fall undefiniert
CWG 2879 C++17 Temporäre Materialisierungskonvertierung wurde auf Prvalue
als Operand eines Operators angewendet, der 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 undefiniert
CWG 2901 C++98 Das Ergebnis der Lvalue-zu-Rvalue-Konvertierung von einem unsigned int
Lvalue, der sich auf ein int Objekt mit dem Wert - 1 bezieht, war unklar
klargestellt

Siehe auch

C-Dokumentation für Implizite Konvertierungen