Неявне перетворення заборонено при поверненні


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;
  • коли вираз використовується в операторі комутатора (T2 є інтегральним типом);
  • коли вираз використовується в операторі if або циклі (T2 - bool).

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().


4

З документів C ++ :

Коли об'єкт типу необов'язково <T> є контекстуально перетворюються в BOOL, повертає перетворення істинно , якщо об'єкт містить значення , і помилково , якщо воно не містить значення.

Про контекстуальні перетворення читайте тут :

У наступних контекстах очікується тип bool і неявне перетворення виконується, якщо декларація bool t (e); добре сформована (тобто, явна функція перетворення, така як явний T :: оператор bool () const; розглядається). Кажуть, що такий вираз e контекстуально перетворюється на 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};
darune

3
@darune return {opt};не працюватиме , але return static_cast<bool>(opt);і return bool{opt};буде працювати. Однак пропонується використовувати has_valueфункцію члена, оскільки вона дійсно показує чіткий намір того, що ви хочете зробити
NutCracker

Або знаменитий return !!pot;хак ( has_valueкраще)
LF

1

Це тому, що неявне покриття std :: необов'язково для bool не підтримується: https://en.cppreference.com/w/cpp/utility/optional/operator_bool

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

Ви повинні явно перетворити на bool як bool(opt)або просто використовувати opt.has_value()замість цього.


bool {opt} також працює, і його слід віддавати перевагу перед bool (opt)
darune

1

Це насправді не про неявну конверсію, це про тип ініціалізації.

Необов’язковою є функція явного перетворення, тобто

explicit operator bool() const; 

Від N4849 [class.conv.fct] / p2

Функція перетворення може бути явною (9.2.2), і в цьому випадку вона розглядається лише як визначене користувачем перетворення для прямої ініціалізації.

Вищезазначене означає, що в цих випадках буде використана функція перетворення: [dcl.init] / p16

Ініціалізація, що виникає (16.1) - для ініціалізатора, який є вкрученим списком виразів, або списком з дужкою-init, (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), називається ініціалізацією копіювання.

Приклад у питанні підпадає під випадок ініціалізації копіювання та не використовує функцію перетворення необов'язкової.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.