Неявное преобразование не допускается при возврате

21
#include <optional>

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

Не компилируется: 'return': cannot convert from 'std::optional<int>' to 'bool'

Консультационная справка Я бы подумал найти объяснение, но я прочитал его, как должно быть в порядке.

Неявные преобразования выполняются всякий раз, когда выражение некоторого типа T1 используется в контексте, которое не принимает этот тип, но принимает некоторый другой тип T2; в частности:

  • когда выражение используется в качестве аргумента при вызове функции, объявленной с параметром T2;
  • когда выражение используется в качестве операнда с оператором, который ожидает T2;
  • при инициализации нового объекта типа T2, включая оператор return в функции, возвращающей T2;
  • когда выражение используется в операторе switch (T2 является целочисленным типом);
  • когда выражение используется в операторе if или цикле (T2 - bool).
darune
источник
7
« Неявные преобразования выполняются» , но operator bool()из std::optionalэто explicit.
Jarod42

Ответы:

22

std::optionalне имеет возможности для неявного преобразования в bool. (Разрешение неявных преобразований boolобычно считается плохой идеей, поскольку boolэто целочисленный тип, поэтому что-то вроде int i = optбы скомпилирует и сделает совсем не то, что нужно.)

std::optional действительно есть «контекстное преобразование» в BOOL, определение которого похоже на оператор литого: explicit operator bool(). Это не может быть использовано для неявных преобразований; он применяется только в определенных конкретных ситуациях, когда ожидаемый «контекст» является логическим, например, условие if-оператора.

То, что вы хотите opt.has_value().

Sneftel
источник
4

Из документов C ++ :

Когда объект типа необязательно <T> является контекстуально преобразуются в BOOL, возвращает преобразование истинно , если объект содержит значение , и ложно , если оно не содержит значение.

Читайте о контекстных конверсиях здесь :

В следующих контекстах ожидается тип bool, и неявное преобразование выполняется, если объявление bool t (e); корректно сформирован (то есть рассматривается явная функция преобразования, такая как явный оператор T :: bool () const;). Такое выражение е, как говорят, контекстуально преобразуется в bool.

  • управляющее выражение if, while, for;
  • операнды встроенных логических операторов!, && и ||;
  • первый операнд условного оператора?:;
  • предикат в объявлении static_assert;
  • выражение в спецификаторе noexcept;
  • выражение в явном спецификаторе;

Вы можете сделать следующее взломать:

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

потому что контекстное преобразование происходит в случае встроенных логических операторов, но контекстное преобразование не включает в себя returnоператоры и std::optionalсамо по себе не имеет неявного преобразования в bool.

Поэтому было бы лучше использовать std::optional<T>::has_value:

bool f() {
    std::optional<int> opt;
    return opt.has_value();
}
Щелкунчик
источник
о чем return {opt}? илиreturn bool{opt};
Дарью
3
@darune return {opt}; не будет работать , но return static_cast<bool>(opt);и return bool{opt};будет работать. Тем не менее, предлагается использовать has_valueфункцию-член, потому что она действительно показывает четкое намерение того, что вы хотите сделать
NutCracker
Или знаменитый return !!pot;хак ( has_valueлучше)
LF
1

Это связано с тем, что неявное скрытие std :: option для bool не поддерживается: https://en.cppreference.com/w/cpp/utility/optional/operator_bool

явный оператор constexpr bool () const noexcept;

Вы должны явно преобразовать в bool как bool(opt)или просто использовать opt.has_value()вместо этого.

theWiseBro
источник
bool {opt} также работает и должен быть предпочтительнее, чем bool (opt)
darune
1

Дело не в неявном преобразовании, а в типе инициализации.

Что необязательно, так это явная функция преобразования, т.е.

explicit operator bool() const; 

От N4849 [class.conv.fct] / p2

Функция преобразования может быть явной (9.2.2), и в этом случае она рассматривается только как пользовательское преобразование для прямой инициализации.

Вышеуказанное означает, что в этих случаях будет использоваться функция преобразования: [dcl.init] / p16

Происходящая инициализация (16.1) - для инициализатора, являющегося списком выражений в скобках или фигурным списком инициализации, (16.2) - для нового инициализатора (7.6.2.7), (16.3) - в выражении static_cast ( 7.6.1.8), (16.4) - при преобразовании типа функциональной нотации (7.6.1.3) и (16.5) - в форме условного списка-инициализации условия называется прямой инициализацией.

Однако в этих случаях не будет использоваться функция преобразования: [dcl.init] / p15

Инициализация, которая происходит в форме = инициализатора или условия скобки или равенства (8.5), а также при передаче аргумента, возврате функции, генерации исключения (14.2), обработке исключения (14.4) и инициализации члена (9.4.1), называется инициализацией копирования.

Пример в вопросе относится к случаю инициализации копирования и не использует опциональную функцию преобразования.

Трикси
источник