不允许隐式转换返回


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的新对象时,包括返回T2的函数中的return语句;
  • 当在switch语句中使用该表达式时(T2是整数类型);
  • 在if语句或循环中使用该表达式时(T2为bool)。

7
隐式转换是执行”,但operator bool()std::optionalexplicit
Jarod42

Answers:


22

std::optional没有任何隐式转换为的功能bool。(bool由于bool将其隐式转换为整数类型,因此通常允许将其隐式转换为一个坏主意,因此诸如此类的内容int i = opt将编译并完全做错了。)

std::optional 确实有到bool的“上下文转换”,其定义看起来类似于强制转换运算符explicit operator bool()。这不能用于隐式转换。它仅适用于预期的“上下文”是布尔值的某些特定情况,例如if语句的条件。

你想要的是opt.has_value()


4

从C ++ docs

当类型的对象可选的<T>上下文转换到布尔,转换返回true,如果对象包含一个值和假,如果它不包含一个值。

在此处阅读有关上下文转换的信息

在以下情况下,如果声明为bool t(e);则为布尔类型,并且将执行隐式转换。格式正确(即考虑使用显式转换函数,例如显式T :: operator 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;hack(has_value更好)
LF

1

这是因为不支持对bool的std :: optional隐式覆盖: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

在static_cast表达式(16.1)中-对于带括号的表达式列表或括号初始化列表的初始化器(16.2)-对于新初始化器(7.6.2.7)(16.3)- (7.6.1.8),(16.4)—在功能符号类型转换(7.6.1.3)和(16.5)中—在条件的括号初始列表形式中被称为直接初始化。

但是,这些情况将不使用转换函数:[dcl.init] / p15

以大括号或等于初始化器或条件的形式(8.5)进行的初始化,以及参数传递,函数返回,引发异常(14.2),处理异常(14.4)和成员初始化的形式(9.4.1),称为复制初始化。

问题中的示例属于复制初始化的情况,并且不使用可选的转换功能。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.