Implizite Konvertierung bei Rückgabe nicht zulässig


21
#include <optional>

bool f() {
  std::optional<int> opt;
  return opt;
}

Kompiliert nicht: 'return': cannot convert from 'std::optional<int>' to 'bool'

Beratungsreferenz Ich hätte gedacht, eine Erklärung zu finden, aber ich habe sie gelesen, da es in Ordnung sein sollte.

Implizite Konvertierungen werden immer dann durchgeführt, wenn ein Ausdruck eines Typs T1 in einem Kontext verwendet wird, der diesen Typ nicht akzeptiert, aber einen anderen Typ T2 akzeptiert. bestimmtes:

  • wenn der Ausdruck als Argument beim Aufrufen 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 der 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).

7
" Implizite Konvertierungen werden durchgeführt" , aber operator bool()von std::optionalist explicit.
Jarod42

Antworten:


22

std::optionalhat keine Möglichkeit zur impliziten Konvertierung in bool. (Das Zulassen impliziter Konvertierungen boolwird im Allgemeinen als schlechte Idee angesehen, dabool es sich um einen integralen Typ handelt, bei dem so etwas int i = optkompiliert und völlig falsch gemacht wird.)

std::optional tut eine "kontextbezogene Konvertierung" in bool, deren Definition einem Cast-Operator ähnelt : explicit operator bool(). Dies kann nicht für implizite Konvertierungen verwendet werden. Dies gilt nur in bestimmten Situationen, in denen der erwartete "Kontext" boolesch ist, wie z. B. die Bedingung einer if-Anweisung.

Was du willst ist opt.has_value().


4

Aus C ++ - Dokumenten :

Wenn ein Objekt vom Typ optional <T> ist kontextuell umgewandelt zu Bool, enthält die Umwandlung gibt true zurück , wenn das Objekt einen Wert und falsch , wenn es keinen Wert enthält.

Lesen Sie mehr über kontextuelle Conversions hier :

In den folgenden Kontexten wird der Typ bool erwartet und die implizite Konvertierung wird durchgeführt, wenn die Deklaration bool t (e); ist wohlgeformt (dh eine explizite Konvertierungsfunktion wie der explizite T :: -Operator bool () const; wird berücksichtigt). Ein solcher Ausdruck e soll kontextuell in bool umgewandelt werden.

  • der kontrollierende 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 expliziten Bezeichner;

Sie können den folgenden Hack ausführen:

bool f() {
    std::optional<int> opt;
    return opt || false;
}

weil kontextuelle Umwandlung geschieht im Fall der integrierten in logischen Operatoren, aber Kontext Konvertierung direkt nicht enthält returnAussagen und std::optionalselbst tut nicht hat implizite Konvertierung zu bool.

Daher ist es am besten, Folgendes zu verwenden std::optional<T>::has_value:

bool f() {
    std::optional<int> opt;
    return opt.has_value();
}

was ist mit return {opt}? oderreturn bool{opt};
Darune

3
@ Darune return {opt};wird nicht funktionieren, aber return static_cast<bool>(opt);oder return bool{opt};würde funktionieren. Es wird jedoch empfohlen, die has_valueMember-Funktion zu verwenden, da sie wirklich die klare Absicht zeigt, was Sie tun möchten
NutCracker

Oder der berühmte return !!pot;Hack ( has_valueist besser)
LF


1

Hier geht es nicht wirklich um implizite Konvertierung, sondern um die Art der Initialisierung.

Was optional ist, ist eine explizite Konvertierungsfunktion, dh

explicit operator bool() const; 

Von N4849 [class.conv.fct] / p2

Eine Konvertierungsfunktion kann explizit sein (9.2.2). In diesem Fall wird sie nur als benutzerdefinierte Konvertierung für die Direktinitialisierung betrachtet.

Das Obige bedeutet, dass diese Fälle die Konvertierungsfunktion verwenden: [dcl.init] / p16

Die Initialisierung, die auftritt (16.1) - für einen Initialisierer, der eine Ausdrucksliste in Klammern oder eine Klammer-Init-Liste ist, (16.2) - für einen Neuinitialisierer (7.6.2.7), (16.3) - in einem static_cast-Ausdruck ( 7.6.1.8), (16.4) - in einer funktionalen Notationstypkonvertierung (7.6.1.3) und (16.5) - in der Form einer Klammer-Init-Liste einer Bedingung wird als direkte Initialisierung bezeichnet.

In diesen Fällen wird jedoch die Konvertierungsfunktion nicht verwendet: [dcl.init] / p15

Die Initialisierung, die in Form eines Klammer-oder-Gleich-Initialisierers oder einer Bedingung (8.5) sowie bei der Übergabe von Argumenten, der Funktionsrückgabe, dem Auslösen einer Ausnahme (14.2), der Behandlung einer Ausnahme (14.4) und der Elementinitialisierung erfolgt (9.4.1) wird als Kopierinitialisierung bezeichnet.

Das Beispiel in der Frage fällt unter den Fall der Kopierinitialisierung und verwendet nicht die Konvertierungsfunktion von optional.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.