为什么 std::visit 必须有单一返回类型?

2024-01-27

玩耍的同时std::variant and std::visit出现了以下问题:

考虑以下代码:

using Variant = std::variant<int, float, double>;

auto lambda = [](auto&& variant) {
  std::visit(
    [](auto&& arg) {
      using T = std::decay_t<decltype(arg)>;
      if constexpr (std::is_same_v<T, int>) {
        std::cout << "int\n";
      } else if (std::is_same_v<T, float>) {
        std::cout << "float\n";
      } else {
        std::cout << "double\n";
      }
    },
  variant);
};

它工作得很好,如以下示例所示:

lambda(Variant(4.5));    // double
lambda(Variant(4.f));    // float
lambda(Variant(4));      // int

那么为什么以下会失败:

using Variant = std::variant<int, float, double>;

auto lambda = [](auto&& variant) {
  std::visit([](auto&& arg) { return arg; }, variant);
};

auto t = lambda(Variant(4.5));

由于静态断言

static_assert failed due to requirement '__all<is_same_v<int
      (*)(__value_visitor<(lambda at main.cc:25:7)> &&,
      __base<std::__1::__variant_detail::_Trait::_TriviallyAvailable, int, float,
      double> &), float (*)(__value_visitor<(lambda at main.cc:25:7)> &&,
      __base<std::__1::__variant_detail::_Trait::_TriviallyAvailable, int, float,
      double> &)>, is_same_v<int (*)(__value_visitor<(lambda at main.cc:25:7)> &&,
      __base<std::__1::__variant_detail::_Trait::_TriviallyAvailable, int, float,
      double> &), double (*)(__value_visitor<(lambda at main.cc:25:7)> &&,
      __base<std::__1::__variant_detail::_Trait::_TriviallyAvailable, int, float,
      double> &)> >::value' "`std::visit` requires the visitor to have a single
      return type."

std::visit显然可以推导出类型arg正如成功的例子所示。那么为什么要求有单一返回类型呢?

编译器是Apple LLVM version 10.0.1 (clang-1001.0.46.4) but gcc version 8.3.0失败并出现类似错误。


返回类型为std::visit仅取决于访问者的类型和传递给它的变体。这就是 C++ 类型系统的工作原理。

如果你想std::visit要返回一个值,该值需要在编译时就具有类型,因为所有变量和表达式在 C++ 中都具有静态类型。

事实上,您通过了Variant(4.5)(因此“显然访问将返回双精度”)在该特定行中不允许编译器改变类型系统的规则 -std::visit return type根本无法根据变体进行更改value您通过了,并且不可能仅从type访客和type的变体。其他一切都会产生极其奇怪的后果。

This https://en.wikipedia.org/wiki/Type_system#Static_type_checking维基百科文章实际上基本上讨论了您所遇到的确切情况/问题,只是用了一个if而不是更复杂的std::visit版本:

例如,考虑一个包含以下代码的程序:

if <complex test> then <do something> else <signal that there is a type error>

即使表达式在运行时始终计算为 true,大多数类型检查器也会将程序视为错误类型而拒绝,因为静态分析器很难(如果不是不可能)确定不会采用 else 分支。


如果你希望返回的类型是“variant-ish”,你必须坚持std::variant。例如,您仍然可以这样做:

auto rotateTypes = [](auto&& variant) {
  return std::visit(
    [](auto&& arg) -> std::variant<int, float, double> {
      using T = std::decay_t<decltype(arg)>;
      if constexpr (std::is_same_v<T, int>) {
        return float(arg);
      } else if (std::is_same_v<T, float>) {
        return double(arg);
      } else {
        return int(arg);
      }
    },
  variant);
};

推导的返回类型std::visit那么就是std::variant<int, float, double>- 只要您不决定一种类型,您就必须保留在变体中(或在单独的模板实例化中)。您不能“欺骗”C++ 放弃使用变体上的身份访问者的静态类型。

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

为什么 std::visit 必须有单一返回类型? 的相关文章

随机推荐

  • 如何使用 xmlhttprequest 从 javascript 将字符串发送到 servlet [重复]

    这个问题在这里已经有答案了 客户端代码 function myReq try var myJSONObject main url http facebook1474159850 altervista org var toServer myJ
  • Mvc3 - 处理(几乎)所有请求所需的数据的最佳实践?

    我正在 mvc3 中创建一个应用程序 想知道如何处理所有应用程序请求所需的数据库数据 其中一些取决于会话 其中一些取决于 url 模式 基本上所有数据都在数据库中 喜欢了解最佳实践 我在应用程序中所做的并认为最佳实践是将通用数据加载到控制器
  • 如何获取 Mercurial 存储库中每个文件的最新版本号列表?

    我为我的 Web 应用程序创建了一个构建系统 该系统重写所有资源 url 以包含文件的修订号 以改进客户端缓存 截至今天 我正在为每个文件运行此命令以获取修订号 hg log template rev n path to file 对每个文
  • 基于 NSFetchedResultsController 的表视图在第二次插入实体时总是失败

    我正在使用 NSFetchedResultsController 来管理在具有一个部分的表视图中显示获取的托管对象 该表一开始是空的 用户可以使用 UI 向其中添加新实体 就目前情况而言 程序在添加第一个实体时始终有效 而在添加第二个实体时
  • C# 中的心电图数字信号处理

    我正在寻找用于数字滤波 低通 高通 陷波 的 C NET 库 以实时过滤心电图波形 有什么建议么 如果这是非商业用途 我听说过关于信号实验室库 http www mitov com html signallab html 非商业用途免费 商
  • MDX - TopCount 加“其他”或“其余”

    我创建了一个 MDX 查询 用于计算前 10 个邮政编码 根据我的患者住院测量 如下所示 WITH MEMBER Discharge Date Y M D Aggregation AS AGGREGATE EXISTING Current
  • 如何在 Github Actions 中查看已取消步骤的日志?

    我的工作流程中有一个步骤是运行命令 python 脚本 这个 python 脚本似乎挂在执行过程中的某个地方 GitHub 显示该步骤在运行时被卡住并且没有任何反应 为了调试这个 我想查看 python 脚本的日志输出 我怎样才能做到这一点
  • PHP 中的测试驱动开发

    我是一名使用 PHP 工作的 Web 开发人员 我在 C 桌面应用程序中使用测试驱动开发的经验有限 在这种情况下 我们使用 nUnit 作为单元测试框架 我想在新项目中开始使用 TDD 但我真的不知道从哪里开始 对于基于 PHP 的单元测试
  • 通知在 flutter 上显示两次

    我被困住了 我的后台通知显示两次 但前台只有一个通知 这是我的代码 Future
  • 谷歌数据存储中的节点分页

    我在使用 Google Datastore 进行分页时遇到问题 我有一个查询 没有限制 有几百个结果 我想检索 5 个 将它们发送回用户 如果用户想要更多 他们会检索下 5 个 根据文档 我创建了查询 var query datastore
  • div 相对于窗口的位置?

    尝试找到 div 相对于窗口的位置 我有一个水平 div 我想获取相对于窗口的左侧值 因此 如果我将第二个 div 滚动到窗口左侧 它将显示 0 不确定如果没有父 div 这是否可行 这是我的小提琴 http jsfiddle net FS
  • 如何在 Symfony2 配置中添加带有值的数组?

    我想在配置文件 config yml 中添加一个简单的值列表 例如 my bundle columns col1 col2 将节点添加到配置解析器时 它只是失败 rootNode treeBuilder gt root my bundle
  • NHibernate 测试,模拟 ISession

    我正在使用 NHibernate 和 Rhinomocks 但在测试我想要的东西时遇到了困难 我想在不访问数据库的情况下测试以下存储库方法 其中 session 作为 ISession 注入存储库 public class Reposito
  • SQL语句只删除一行重复项

    所以我正在使用 Ruby 工作 并假设我的两列表中有 6 行完全相同 就我而言 我的表 campaign items 有两列 campaign name 和 item 我想使用单个查询仅删除 6 个重复项中的一行 我是这样开始的 db ex
  • Flex 页脚在 Chrome 中不会停留在底部

    仅当内容短于视口时 我才使用 Flexbox 让页脚保持在底部 如果它较高 页脚应保持在内容下方 以便您必须滚动才能看到它 这在 Firefox 和 Edge 中可以正常工作 但在 Chrome 或 IE 中则不行 在 Chrome 中 正
  • WCF - 自定义凭据和安全令牌

    我对 WCF 开发相当陌生 在学习该框架时遇到了一些问题 我有一个必须支持 REST 和 SOAP 的服务 API 到目前为止 这很容易实现 尤其是使用 WCF4 和路由 我目前正在研究授权 并通过创建两个新的管理器类来扩展 Authori
  • 如何在Apache中设置mod_lua来访问第三方Lua模块?

    我正在尝试为 Apache 设置 mod lua 模块 但在访问第三方 Lua 模块时遇到了困难 假设我在 Apache 的 htdocs 文件夹中有一个 hello world lua 其中包含以下内容 require apache2 f
  • 尽管已传输,但仍出现错误“无法传输文件 *.jar,状态代码为 409”

    我正在尝试将项目发布到azure artefacts 项目pom是这样的
  • Django 使用 selenium 进行测试,未加载固定装置

    我正在使用 Selenium 为 Django 网站设置功能测试 我有一个固定文件 users fixtures users json 并在另一个应用程序的功能测试中使用它 accounts 运行测试时 我还运行我的开发服务器来接受来自 S
  • 为什么 std::visit 必须有单一返回类型?

    玩耍的同时std variant and std visit出现了以下问题 考虑以下代码 using Variant std variant