Conversion implicite non autorisée au retour

21
#include <optional>

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

Ne compile pas: 'return': cannot convert from 'std::optional<int>' to 'bool'

Consultation de référence J'aurais pensé trouver une explication, mais je l'ai lu comme il se doit.

Des conversions implicites sont effectuées chaque fois qu'une expression d'un certain type T1 est utilisée dans un contexte qui n'accepte pas ce type, mais accepte un autre type T2; en particulier:

  • lorsque l'expression est utilisée comme argument lors de l'appel d'une fonction déclarée avec T2 comme paramètre;
  • lorsque l'expression est utilisée comme opérande avec un opérateur qui attend T2;
  • lors de l'initialisation d'un nouvel objet de type T2, y compris l'instruction return dans une fonction retournant T2;
  • lorsque l'expression est utilisée dans une instruction switch (T2 est de type intégral);
  • lorsque l'expression est utilisée dans une instruction if ou une boucle (T2 est bool).
Darune
la source
7
« Implicite conversions sont effectuées » , mais operator bool()de std::optionalest explicit.
Jarod42

Réponses:

22

std::optionaln'a aucune possibilité de conversion implicite en bool. (Autoriser les conversions implicites en boolest généralement considéré comme une mauvaise idée, car il bools'agit d'un type intégral, donc quelque chose comme int i = optcela se compilerait et ferait complètement la mauvaise chose.)

std::optional ne une « conversion contextuelle » bool, dont la définition ressemble à un opérateur de cast: explicit operator bool(). Cela ne peut pas être utilisé pour les conversions implicites; elle ne s'applique que dans certaines situations spécifiques où le "contexte" attendu est un booléen, comme la condition d'une instruction if.

Ce que tu veux c'est opt.has_value().

Sneftel
la source
4

De C + docs :

Lorsqu'un objet de type facultatif <T> est converti contextuellement en bool, la conversion renvoie true si l'objet contient une valeur et false s'il ne contient pas de valeur.

Découvrez les conversions contextuelles ici :

Dans les contextes suivants, le type bool est attendu et la conversion implicite est effectuée si la déclaration bool t (e); est bien formé (c'est-à-dire, une fonction de conversion explicite telle que T :: operator bool () const explicite; est considérée). On dit que cette expression e est contextuellement convertie en bool.

  • l'expression de contrôle de if, while, for;
  • les opérandes des opérateurs logiques intégrés!, && et ||;
  • le premier opérande de l'opérateur conditionnel?:;
  • le prédicat dans une déclaration static_assert;
  • l'expression dans un spécificateur noexcept;
  • l'expression dans un spécificateur explicite;

Vous pouvez faire le hack suivant:

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

car la conversion contextuelle se produit dans le cas des opérateurs logiques intégrés, mais la conversion contextuelle n'inclut pas d' returninstructions et std::optionaln'a pas en soi de conversion implicite vers bool.

Par conséquent, il serait préférable d'utiliser std::optional<T>::has_value:

bool f() {
    std::optional<int> opt;
    return opt.has_value();
}
Casse Noisette
la source
qu'en est-il return {opt}? oureturn bool{opt};
darune
3
@darune return {opt};ne fonctionnera pas , mais return static_cast<bool>(opt);ou return bool{opt};ne fonctionnerait. Cependant, il est suggéré d'utiliser la has_valuefonction membre car elle montre vraiment l'intention claire de ce que vous voulez faire
NutCracker
Ou le célèbre return !!pot; hack ( has_valuec'est mieux)
LF
1

En effet, la conversion implicite de std :: facultatif en booléen n'est pas prise en charge: https://en.cppreference.com/w/cpp/utility/optional/operator_bool

opérateur explicite constexpr bool () const noexcept;

Vous devez explicitement convertir en bool as bool(opt)ou simplement utiliser à la opt.has_value()place.

theWiseBro
la source
bool {opt} fonctionne également et devrait être préféré à bool (opt)
darune
1

Il ne s'agit pas vraiment de conversion implicite, il s'agit du type d'initialisation.

Ce qui est facultatif est une fonction de conversion explicite, c'est-à-dire

explicit operator bool() const; 

Depuis N4849 [class.conv.fct] / p2

Une fonction de conversion peut être explicite (9.2.2), auquel cas elle n'est considérée que comme une conversion définie par l'utilisateur pour l'initialisation directe.

Ce qui précède signifie que ces cas utiliseront la fonction de conversion: [dcl.init] / p16

L'initialisation qui se produit (16.1) - pour un initialiseur qui est une liste d'expressions entre parenthèses ou une liste d'initiation contreventée, (16.2) - pour un nouvel initialiseur (7.6.2.7), (16.3) - dans une expression static_cast ( 7.6.1.8), (16.4) - dans une conversion de type de notation fonctionnelle (7.6.1.3), et (16.5) - sous la forme de liste d'initiation contreventée d'une condition est appelée initialisation directe.

Cependant, ces cas n'utiliseront pas la fonction de conversion: [dcl.init] / p15

L'initialisation qui se produit sous la forme = d'une initialisation ou condition d'accolade ou égale (8.5), ainsi que lors du passage d'arguments, du retour de fonction, du lancement d'une exception (14.2), du traitement d'une exception (14.4) et de l'initialisation des membres (9.4.1), est appelé copie-initialisation.

L'exemple de la question relève du cas d'initialisation de la copie et n'utilise pas la fonction de conversion facultative.

Trixie
la source