为什么必须STD ::访问具有单一返回类型
Why must std::visit have a single return type?
在使用std::variant
和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 << "intn";
} else if (std::is_same_v<T, float>) {
std::cout << "floatn";
} else {
std::cout << "doublen";
}
},
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)
,但gcc version 8.3.0
失败了。
std::visit
的返回类型仅取决于访问者的类型,而变体传递给了它。这仅仅是C 类型系统的工作方式。
如果您希望 std::visit
返回一个值,则该值需要在编译时具有类型,因为所有变量和表达式在C 中具有静态类型。
您通过 Variant(4.5)
的事实(显然,访问将返回双重&quot"该行中的double d&quort'不允许编译器弯曲类型系统的规则-std::visit
返回 type type 根本无法根据您通过的变体 value 更改,并且不可能仅从访问者的 type 和<em决定一个返回类型。>变体的类型。其他一切都会带来极其奇怪的后果。
这篇Wikipedia文章实际上基本上讨论了您所面临的确切情况/问题,而不是使用if
而不是更精致的std::visit
版本:
例如,考虑包含代码的程序:
if <complex test> then <do something> else <signal that there is a type error>
即使表达式在运行时始终评估为true,大多数类型的检查器都会拒绝该程序不易于否决,因为静态分析仪很难(如果不是不可能(确定其他分支不会是拍摄。
如果您希望返回的类型为"变体",则必须坚持使用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>
-只要您不决定一种类型,就必须保持在变体中(或在单独的模板实例化中(。您不能Quot;C 在变体上放弃使用身份访问者的静态键入。
,尽管每个"实现"都是不同的超载,因此可能具有不同的返回类型,在某个时候,您需要一个共同的访问点,而通用的访问点将需要一个单个返回类型,因为所选变体类型仅在运行时已知。
与访客的常见约定可以在visit
代码中执行该逻辑;实际上,std::visit
的目的是为您做所有这些魔术并抽象运行时类型切换。
否则,您基本上将卡住在呼叫的std::visit
。
很容易想到可以使用模板修复所有这些方法:毕竟,您使用了通用的lambdas,因此所有这些过载都是自主实例的,那么为什么返回类型不能只是"知道"呢?同样,它只有在运行时才知道,所以这对您来说是不好的。必须有一些静态的方式将访问结果传递给呼唤。
- 如何获取std::result_of函数的返回类型
- 奇怪的结构&GCC&clang(void*返回类型)
- 如何建立使用模板函数的lambda函数的尾部返回类型
- 为什么与常规GCC不同,即使有"学究性错误",MinGW-GCC也能容忍丢失的返回类型
- 在没有定义返回类型的函数中返回布尔值,并将结果保存在无错误的char编译中-为什么
- 特征::矩阵<双精度,1,3> 结构类型函数中的返回类型函数
- 函数作为模板参数,是否对返回类型强制约束
- C++中函数的向量返回类型引发错误
- 检查函数返回类型是否与STL容器类型值相同
- 为什么返回类型中需要typename?C++
- <Windows>为什么 std::thread::native_handle 返回类型为"long long unsigned int"的值,而不是 void*(又名 HANDLE)?
- 警告:在函数返回类型 [-Wignore 限定符] 时忽略类型限定符
- 为什么 c++(g++) 不允许模板返回类型和函数名称之间有空格?
- 为什么返回类型的'const'限定符对标有 __forceinline/内联的函数没有影响?
- 推导 std::vector::back() 的返回类型
- 在 c++ 中将函数返回类型指定为模板参数
- 使用 SWIG 更改生成的 CS 函数中的返回类型
- QtQuick - qml:28:错误:未知方法返回类型:自定义类型
- 基于返回类型的转换和过载扣除
- 为什么必须STD ::访问具有单一返回类型