为什么std :: visit必须具有单个返回类型? [英] Why must std::visit have a single return type?

查看:69
本文介绍了为什么std :: visit必须具有单个返回类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在玩 std :: variant std :: visit 时,出现了以下问题:

While playing around with std::variant and std::visit the following question came up:

考虑以下代码:

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);
};

它可以正常工作,如以下示例所示:

It works fine as the following examples show:

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

然后以下原因为何失败:

Then why does the following fail:

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 。那么为什么要求具有单一返回类型呢?

std::visit can obviously deduce the type of arg as the successful example shows. Then why the requirement to have a single return type?

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

Compiler is Apple LLVM version 10.0.1 (clang-1001.0.46.4) but gcc version 8.3.0 fails with a similar error.

推荐答案

std :: visit 的返回类型仅取决于访问者的类型以及传递给它的变量。

The return type of std::visit depends only on the types of the visitor and the variant passed to it. That's simply how the C++ type system works.

如果要 std :: visit 返回一个值,则该值必须具有

If you want std::visit to return a value, that value needs to have a type at compile-time already, because all variables and expressions have a static type in C++.

您传递 Variant(4.5)(因此,显然访问将返回双精度)在该特定行中不允许编译器更改类型系统的规则- std :: visit return type 根本无法根据您传递的变量 value 进行更改,并且不可能仅从 type <访问者的/ em>和变体的类型

The fact that you pass a Variant(4.5) (so "clearly the visit would return a double") in that particular line doesn't allow the compiler to bend the rules of the type system - the std::visit return type simply cannot change based on the variant value that you pass, and it's impossible to decide on exactly one return type only from the type of the visitor and the type of the variant. Everything else would have extremely weird consequences.

维基百科文章实际上基本上讨论了您的确切情况/问题,只是使用 if 而不是更详细的 std :: visit 版本:

This wikipedia article actually discusses basically the exact situation/question you have, just with an if instead of the more elaborate std::visit version:


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

For example, consider a program containing the code:

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

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

Even if the expression always evaluates to true at run-time, most type checkers will reject the program as ill-typed, because it is difficult (if not impossible) for a static analyzer to determine that the else branch will not be taken.




如果您希望返回的类型为 variant-ish,则必须坚持使用 std :: variant 。例如,您仍然可以这样做:


If you want the returned type to be "variant-ish", you have to stick with std::variant. For example, you could still do:

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 ++放弃了使用变体中的身份访问者进行静态输入。

The deduced return type of std::visit then is std::variant<int, float, double> - as long as you don't decide on one type, you must stay within a variant (or within separate template instantiations). You cannot "trick" C++ into giving up static typing with an identity-visitor on a variant.

这篇关于为什么std :: visit必须具有单个返回类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆