为什么 std::Optional 的强制转换运算符会被忽略?

2024-01-17

这段代码

#include <iostream>
#include <optional>

struct foo
{
    explicit operator std::optional<int>() {
        return std::optional<int>( 1 );
    }
    explicit operator int() {
        return 2;
    }
};

int main()
{
    foo my_foo;

    std::optional<int> my_opt( my_foo );
    std::cout << "constructor: " << my_opt.value() << std::endl;

    my_opt = static_cast<std::optional<int>>(my_foo);
    std::cout << "static_cast: " << my_opt.value() << std::endl;
}

产生以下输出 https://wandbox.org/permlink/Et7gN95oNk9xva5W

constructor: 2
static_cast: 2

在 Clang 4.0.0 和 MSVC 2017 (15.3) 中。 (现在让我们忽略 GCC,因为它的行为似乎是buggy https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81952在这种情况下。)

为什么输出是2?我希望1。的构造函数std::optional似乎更喜欢铸造而不是内部类型(int)尽管事实是强制转换为外部类型(std::optional<int>)可用。根据 C++ 标准,这是否正确?如果是这样,标准是否有理由不要求尝试强制转换为外部类型?我会发现这更合理,并且可以想象它是使用来实现的enable_if and is_convertible如果可以转换到外部类型,则禁用该构造函数。否则每个强制转换运算符std::optional<T>在用户类中 - 即使它是完美匹配 - 如果还有一个要匹配,原则上也会被忽略T。我会觉得这很令人讨厌。

我发布了一个有点类似的问题 https://stackoverflow.com/questions/45843428/different-results-in-clang-and-gcc-when-casting-to-stdoptionalt昨天,但可能没有准确地陈述我的问题,因为最终的讨论更多是关于 GCC 错误。这就是为什么我在这里再次更明确地询问。


如果巴里的出色回答仍然不清楚,这是我的版本,希望对您有所帮助。

最大的问题是为什么用户定义的转换不是optional<int>直接初始化的首选:

    std::optional<int> my_opt(my_foo);

毕竟有一个构造函数optional<int>(optional<int>&&)以及用户定义的转换my_foo to optional<int>.

原因是构造函数模板,应该在以下情况下激活T (int) 可构造自U and U两者都不是std::in_place_t nor optional<T>,并直接初始化T从中。事情就是这样,消灭了optional(foo&).

最终生成的optional<int>看起来像:

class optional<int> {
    . . .
    int value_;
    . . .
    optional(optional&& rhs);
    optional(foo& rhs) : value_(rhs) {}
    . . .

optional(optional&&)需要用户定义的转换,而optional(foo&)完全匹配my_foo。所以它赢了,直接初始化int from my_foo。只有此时operator int()选择更好的匹配来初始化int。结果就变成了2.

2) 如果出现以下情况my_opt = static_cast<std::optional<int>>(my_foo),虽然它sounds like "初始化my_opt就好像它是std::optional<int>”,其实means "创建一个临时的std::optional<int> from my_foo并从中移动分配” 如中所述[expr.static.cast]/4 http://eel.is/c++draft/expr.static.cast#4:

If T是引用类型,效果与执行相同 声明和初始化
T t(e);对于一些临时发明的 多变的t([dcl.init]),然后使用临时变量作为 转换的结果。否则,结果对象是 直接初始化自e.

所以就变成了:

    my_opt = std::optional<int>(my_foo);

我们又回到了之前的情况;my_opt随后从临时初始化optional,已经持有2.

转发引用的重载问题是众所周知的。斯科特·迈尔斯在他的书中有效的现代 C++第 26 章广泛讨论了为什么重载“通用引用”是一个坏主意。这样的模板会不知疲倦地消除您扔给它们的任何类型,这将使​​所有内容和任何不完全匹配的内容黯然失色。所以我很惊讶委员会选择了这条路线。


至于原因为什么会这样,在提案中N3793 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3793.html#optional.object.ctor并以标准单位表示2016 年 11 月 15 日 https://github.com/cplusplus/draft/commit/7988d9b5e98875b79ae942c92cb32f9990acd02a确实是

  optional(const T& v);
  optional(T&& v);

但随后作为LWG 缺陷 2451 http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2451它被改为

  template <class U = T> optional(U&& v);

理由如下:

如下所示的代码目前格式不正确(感谢 STL 令人信服的例子):

optional<string> opt_str = "meow";

这是因为它需要两个用户定义的转换(从const char* to string,并从string to optional<string>)其中 语言只允许一种。这可能会是一个惊喜和 给用户带来不便。

optional<T>应该可以从任何隐式转换U那是 隐式转换为T。这可以作为非显式实现 构造函数模板optional(U&&),仅通过 SFINAE 启用 如果is_convertible_v<U, T> and is_constructible_v<T, U>,加上任意 需要附加条件以避免与其他条件产生歧义 构造函数...

最后我认为没关系T排名高于optional<T>,毕竟这是一个相当不寻常的选择may有一个值并且the value.

从性能角度来看,从以下位置进行初始化也有好处:T而不是来自另一个optional<T>. An optional通常实现为:

template<typename T>
struct optional {
    union
    {
        char dummy;
        T value;
    };
    bool has_value;
};

所以初始化它optional<T>&看起来像

optional<T>::optional(const optional<T>& rhs) {
  has_value = rhs.has_value;
  if (has_value) {
    value = rhs.value;
  }
}

而初始化从T&需要更少的步骤:

optional<T>::optional(const T& t) {
  value = t;
  has_value = true;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 std::Optional 的强制转换运算符会被忽略? 的相关文章

  • 使用 lambda 表达式注册类型

    我想知道如何在 UnityContainer 中实现这样的功能 container RegisterType
  • 适合初学者的良好调试器教程[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 有谁知道一个好的初学者教程 在 C 中使用调试器 我感觉自己好像错过了很多 我知道怎么做 单步执行代码并查看局部变量 虽然这常常给我带来问
  • 代码 GetAsyncKeyState(VK_SHIFT) & 0x8000 中的这些数字是什么?它们是必不可少的吗?

    我试图在按下按键的简单动作中找到这些数字及其含义的任何逻辑解释 GetAsyncKeyState VK SHIFT 0x8000 可以使用哪些其他值来代替0x8000它们与按键有什么关系 GetAsyncKeyState 根据文档返回 如果
  • GetType() 在 Type 实例上返回什么?

    我在一些调试过程中遇到了这段代码 private bool HasBaseType Type type out Type baseType Type originalType type GetType baseType GetBaseTyp
  • 在c#中执行Redis控制台命令

    我需要从 Redis 控制台获取 客户端列表 输出以在我的 C 应用程序中使用 有没有办法使用 ConnectionMultiplexer 执行该命令 或者是否有内置方法可以查找该信息 CLIENT LIST是 服务器 命令 而不是 数据库
  • 如何判断计算机是否已重新启动?

    我曾经使用过一个命令行 SMTP 邮件程序 作为试用版的限制 它允许您在每个 Windows 会话中最多接收 10 封电子邮件 如果您重新启动计算机 您可能还会收到 10 个以上 我认为这种共享软件破坏非常巧妙 我想在我的应用程序中复制它
  • 如何填充 ToolStripComboBox?

    我发现它很难将数据绑定到ToolStripComboBox 好像没有这个ValueMember and DisplayMember特性 怎么绑定呢 访问toolstripcombobox中包装的组合框并访问其ValueMember Disp
  • Visual Studio 在构建后显示假错误

    我使用的是 Visual Studio 2017 构建后 sln在调试模式下 我收到错误 但是 当我通过双击错误列表选项卡中的错误来访问错误时 错误会从页面中消失 并且错误数量也会减少 我不太确定这种行为以及为什么会发生这种情况 有超过 2
  • 从客户端访问 DomainService 中的自定义对象

    我正在使用域服务从 Silverlight 客户端的数据库中获取数据 在DomainService1 cs中 我添加了以下内容 EnableClientAccess public class Product public int produ
  • 在Linux中,找不到框架“.NETFramework,Version=v4.5”的参考程序集

    我已经设置了 Visual studio 来在我的 Ubuntu 机器上编译 C 代码 我将工作区 我的代码加载到 VS 我可以看到以下错误 The reference assemblies for framework NETFramewo
  • 将 Long 转换为 DateTime 从 C# 日期到 Java 日期

    我一直尝试用Java读取二进制文件 而二进制文件是用C 编写的 其中一些数据包含日期时间数据 当 DateTime 数据写入文件 以二进制形式 时 它使用DateTime ToBinary on C 为了读取 DateTime 数据 它将首
  • 类型约束

    我有以下类层次结构 class Header IEnumerable
  • IQueryable 单元或集成测试

    我有一个 Web api 并且公开了一个端点 如下所示 api 假期 name name 这是 Web api 的控制器 get 方法 public IQueryable
  • 在 NaN 情况下 to_string() 可以返回什么

    我使用 VS 2012 遇到了非常令人恼火的行为 有时我的浮点数是 NaN auto dbgHelp std to string myFloat dbgHelp最终包含5008角色 你不能发明这个东西 其中大部分为0 最终结果是 0 INF
  • C++ 中的双精度型数字

    尽管内部表示有 17 位 但 IEE754 64 位 浮点应该正确表示 15 位有效数字 有没有办法强制第 16 位和第 17 位为零 Ref http msdn microsoft com en us library system dou
  • 高效列出目录中的所有子目录

    请参阅迄今为止所采取的建议的编辑 我正在尝试使用 WinAPI 和 C 列出给定目录中的所有目录 文件夹 现在我的算法又慢又低效 使用 FindFirstFileEx 打开我正在搜索的文件夹 然后我查看目录中的每个文件 使用 FindNex
  • C++ new * char 不为空

    我有一个问题 我在 ASIO 中开发服务器 数据包采用尖头字符 当我创建新字符时 例如char buffer new char 128 我必须手动将其清理为空 By for int i 0 i lt 128 i buffer i 0x00
  • OpenGL:仅获取模板缓冲区而没有深度缓冲区?

    我想获取一个模板缓冲区 但如果可能的话 不要承受附加深度缓冲区的开销 因为我不会使用它 我发现的大多数资源表明 虽然模板缓冲区是可选的 例如 排除它以利于获得更高的深度缓冲区精度 但我还没有看到任何请求并成功获取仅 8 位模板缓冲区的代码
  • 使用 omp_set_num_threads() 将线程数设置为 2,但 omp_get_num_threads() 返回 1

    我有以下使用 OpenMP 的 C C 代码 int nProcessors omp get max threads if argv 4 NULL printf argv 4 s n argv 4 nProcessors atoi argv
  • Objective-C / C 给出枚举默认值

    我在某处读到过关于给枚举默认值的内容 如下所示 typedef enum MarketNavigationTypeNone 0 MarketNavigationTypeHeirachy 1 MarketNavigationTypeMarke

随机推荐

  • INSERT 语句中出现“此处不允许列”错误

    我创建了这个名为 LOCATION 的表 通过做这个 CREATE TABLE LOCATION POSTCODE VARCHAR 10 PRIMARY KEY STREET NAME VARCHAR 20 CITY VARCHAR 20
  • 在 FastCGI 和 Octave 中重新定义标准输出

    我正在努力在 Ubuntu Linux 中使用 C C 在 FastCGI 会话中实现 Octave 解释器 我遇到的问题是 FCGI 重定向stdout to FCGI 标准输出 但预编译的 Octave 头文件仍然使用正常的stdout
  • Flex:组合框控件的自定义项目渲染器截断文本

    我已经实现了一个自定义项目渲染器 我正在处理的 Flex 项目上将其与组合框一起使用 它显示每个项目的图标和一些文本 唯一的问题是 当文本较长时 菜单的宽度无法正确调整 并且文本在显示时会被截断 我尝试调整所有明显的属性来缓解这个问题 但没
  • 如何将图像保存到sqlite数据库

    在我的课程中 我有一个方法可以在照片库中搜索图像 并接收从手机摄像头拍摄的图像 我现在需要将此图像保存在 sqlite 数据库中 我正在使用像 BLOB 这样的数据库字段 但不像在 bity 中序列化图像或在decode64 中进行转换以写
  • 在 GraphQL 架构中使用数字作为键?

    您可以使用 GraphQL Schema 语言在 GraphQL Schema 中使用数字作为键吗 即 这是一个小片段 type tax code allocation country KOR states 11 tax code allo
  • 将 `:map` 的输出获取到缓冲区

    我想得到无参数的输出 map调用 以便我可以使用 vim 的搜索功能来查找映射 我发现关于 redir 它将 ex 命令的输出重定向到变量 寄存器或文件中 但它似乎不适用于 map 它必须以某种不同的方式输出映射 例如 echo hello
  • 如何使用可旁加载的证书创建 UWP 应用

    我正在尝试创建一个可以侧载到其他电脑上的 UWP 应用程序 我的主要问题是 我是否需要来自可信来源的证书来签署我的应用程序 经过大量研究后 我了解到您可以通过应用程序旁加载 安装 UWP 应用程序应用程序安装程序 https www mic
  • 中介者模式与创建

    我的演示文稿中有几个需要相互交互的 小部件 但交互已经变得足够复杂 需要一个新对象来处理交互 在尝试通过中介者作为该对象进行工作时 我对如何有效地构建参与者感到困惑 中介者必须了解小部件 而小部件也必须了解中介者 使用下面的玩具类 有人可以
  • jquery 日期时间选择器设置 minDate 动态

    我正在使用trentrichardson com 的日期时间选择器 我有一个带有两个输入字段的表单 from and to我希望能够动态地将 minDate 设置为我的 to 字段 等于我的 from 字段的值 我知道我应该使用 befor
  • 从 Unix 时间戳转换为 Groovy 中的日期

    我有一个 unix 时间戳中的日期 我想将其转换为人类可读的 def dateUnix 1486146877214 Date dateObj new Date long dateUnix 1000 def cleanDate new Sim
  • 加载了错误的 Java 资源包

    在我的应用程序中 我使用 java 资源包来翻译其标签 我目前有两个文件 带有英语标签的 resources properties 默认语言 带有法语标签的 resources fr properties 然后 我使用以下命令加载捆绑包属性
  • XSLT - 识别具有相同属性值模式的连续节点

    我有这样的xml section p aa p p bb p p cc p p dd p p ee p p ff p p gg p p hh p p ii p p jj p p xx p p p section
  • 如何从 Java 中的内部 Thread Runnable 方法获取返回值?

    我该如何分配Status with CallMe using isFinish 返回值 true 吗 public static boolean isFinish boolean Status false new Thread new Ru
  • Solr、特殊字符和拉丁文到西里尔文字符转换

    我正在尝试使用 Solr 或 Lucene 设置一个搜索引擎 它可以包含带有特殊字符的拉丁语文本 特殊字符包括 或 作为示例 或西里尔字符 示例包括 或 和 无论如何 我正在尝试找到一个解决方案 让我可以搜索包含这些字符的单词 但对于键盘上
  • 为什么 Angular2(点击)事件没有在

    我发现 Angular2 有一个奇怪的行为 click 没有对此进行射击 div test div 但它在这里有效 div test div 谁能解释这种行为 为什么需要设置位置样式才能 点击 触发 我错过了什么吗 你的代码片段看起来一切都
  • 如何在命令栏中水平对齐AppBarButton

    我想调整我的单曲AppBarButton向右CommandBar in a Page BottomBar 在设计上它展示了app bar button在右侧 但在模拟器中 按钮始终位于中心 有没有办法对齐AppBarButton in a
  • 弹出窗口内的下拉列表(按下按钮时出现)

    目前我正在尝试创建一个按钮 该按钮创建一个弹出框 该弹出框又具有一个下拉列表 例如here http www w3schools com tags tag select asp 但是 我无法弄清楚如何将下拉列表放入弹出窗口中 有人可以帮忙吗
  • 如何反转 RDD.takeOrdered() 的顺序?

    Spark 中 RDD 的 takeOrdered 方法反转顺序的语法是什么 为了奖励积分 Spark 中 RDD 的自定义排序语法是什么 相反的顺序 val seq Seq 3 9 2 3 5 4 val rdd sc paralleli
  • 如何在 Nuxt(或 Vue)中将文本文件中的内容作为字符串读取?

    我想读取我导入的文本文件的内容 vue文件如import ToS from static terms of service txt 我想以字符串形式访问内容 我该怎么做 VUE CLI 3 首先安装原始加载程序npm install raw
  • 为什么 std::Optional 的强制转换运算符会被忽略?

    这段代码 include