【问题标题】:Implicit conversion not allowed on return返回时不允许隐式转换
【发布时间】:2020-05-30 03:47:39
【问题描述】:
#include <optional>

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

不编译:'return': cannot convert from 'std::optional&lt;int&gt;' to 'bool'

咨询参考我本来想找到一个解释,但我读了它应该没问题。

每当某种类型的表达式时,都会执行隐式转换 T1 用于不接受该类型的上下文中,但接受一些 其他类型 T2;特别是:

  • 在调用以 T2 作为参数声明的函数时,使用表达式作为参数时;
  • 当表达式用作操作数时,需要 T2 的运算符;
  • 初始化 T2 类型的新对象时,包括返回 T2 的函数中的 return 语句;
  • 在switch语句中使用表达式时(T2为整型);
  • 当表达式用于 if 语句或循环时(T2 为 bool)。

【问题讨论】:

  • “执行隐式转换”,但std::optional中的operator bool()explicit

标签: c++ language-lawyer c++17 implicit-conversion


【解决方案1】:

std::optional 没有任何工具可以隐式转换为bool。 (允许隐式转换为bool 通常被认为是一个坏主意,因为bool 是一个整数类型,所以像int i = opt 这样的东西会编译并做完全错误的事情。)

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

你想要的是opt.has_value()

【讨论】:

    【解决方案2】:

    来自 C++ docs:

    optional 类型的对象被上下文转换 为 bool 时, 如果对象包含值,则转换返回 true,否则返回 false 如果它不包含值。

    阅读上下文转换here

    在以下上下文中,类型 bool 是预期的,而隐式 如果声明 bool t(e),则执行转换;格式良好 (即显式转换函数,如显式 T::operator 布尔()常量;被认为)。这样的表达式 e 被称为 上下文转换为布尔值。

    • if、while、for 的控制表达式;
    • 内置逻辑运算符的操作数 !、&& 和 ||;
    • 条件运算符的第一个操作数?:;
    • static_assert 声明中的谓词;
    • noexcept 说明符中的表达式;
    • 显式说明符中的表达式;

    您可以执行以下 hack:

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

    因为上下文转换发生在内置逻辑运算符的情况下,但上下文转换包括return语句和std::optional本身有隐式转换为bool

    因此,最好使用std::optional&lt;T&gt;::has_value

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

    【讨论】:

    • return {opt} 呢?或return bool{opt};
    • @darune return {opt}; 不起作用,但 return static_cast&lt;bool&gt;(opt);return bool{opt}; 会起作用。但是,建议使用has_value 成员函数,因为它确实表明了您想要做什么的明确意图
    • 还是著名的return !!pot; hack(has_value 更好)
    【解决方案3】:

    这是因为不支持将 std::optional 隐式覆盖到 bool:https://en.cppreference.com/w/cpp/utility/optional/operator_bool

    constexpr 显式运算符 bool() const noexcept;

    您必须将 bool(opt) 显式转换为 bool 或直接使用 opt.has_value()

    【讨论】:

    • bool{opt} 也可以,应该优先于 bool(opt)
    【解决方案4】:

    这不是关于隐式转换,而是关于初始化的类型。

    可选的是显式转换函数,即

    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) 被调用 复制初始化。

    问题中的例子属于复制初始化情况,没有使用可选的转换函数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-08
      • 1970-01-01
      • 1970-01-01
      • 2022-01-09
      相关资源
      最近更新 更多