Неявное преобразование не допускается при возврате - PullRequest
21 голосов
/ 14 февраля 2020
#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 или al oop (T2 - bool).

Ответы [ 4 ]

22 голосов
/ 14 февраля 2020

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

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

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

4 голосов
/ 14 февраля 2020

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

Когда объект типа необязательный равен , контекстно преобразуется в bool, преобразование возвращает true, если объект содержит значение, и false, если оно не содержит значение.

Подробнее о контекстных преобразованиях здесь :

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

  • управляющее выражение, если, а для;
  • операнды встроенных логических операторов!, && и ||;
  • первый операнд условного оператора?:;
  • предикат в объявлении 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();
}
1 голос
/ 12 апреля 2020

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

Необязательно есть функция явного преобразования, т. Е.

explicit operator bool() const; 

из N4849 [класс. conv.fct] / p2

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

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

Происходящая инициализация (16.1) - для инициализатора, являющегося списком выражений в скобках или braced-init-list, (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

Инициализация tha t происходит в виде = инициализации или условия скобки или равенства (8.5), а также при передаче аргумента, возврате функции, генерации исключения (14.2), обработке исключения (14.4) и инициализации члена (9.4). .1), называется copy-initialization.

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

1 голос
/ 14 февраля 2020

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

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

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

...