具有不同返回类型的变体访客
Variant visitor with different return types
考虑以下使用boost::variant
的代码段(但也应该完全适用于std::variant
(。
#include <vector>
#include <boost/variant.hpp>
int main()
{
boost::variant<std::vector<int>, std::vector<double> > vr
= std::vector<int>(5, 5);;
// OK, no problem.
boost::apply_visitor([](auto a) { std::cout << a[0] << "n"; }, vr);
// ERROR: return types must not differ.
//boost::apply_visitor([](auto a) { return a.begin(); }, vr);
}
在这里,我们有变体,它吃掉不同类型的标准向量(例如,本例中的 int
和 double
(,我们希望有一个返回不同类型的对象的访问者(在本例中,迭代器到底层容器的开头(。但是,这不会像std::vector<int>::iterator
那样编译,显然与std::vector<double>::iterator
不同。有没有一种巧妙的方法可以从根本上实现这一目标,可能通过额外的间接层?
您可以返回不同的变体
#include <iostream>
#include <vector>
#include <boost/variant.hpp>
int main()
{
boost::variant<std::vector<int>, std::vector<double> > vr
= std::vector<int>(5, 5);
using iter_variant = boost::variant<std::vector<int>::iterator, std::vector<double>::iterator >;
using value_variant = boost::variant<int, double>;
// OK, no problem.
boost::apply_visitor([](auto a) { std::cout << a[0] << "n"; }, vr);
// Also OK
boost::apply_visitor([](auto a) -> iter_variant { return a.begin(); }, vr);
// Also OK
boost::apply_visitor([](auto a) -> value_variant { return a[0]; }, vr);
}
现场观看
给定一个泛型 lambda 和一个变体,您可以获得适当的返回类型。
template<typename Func, typename Variant>
struct visitor_result;
template<typename Func, typename ... Ts>
struct visitor_result<Func, boost::variant<Ts...>>
{
using type = boost::variant<decltype(std::declval<Func>()(std::declval<Ts>()))...>;
};
template<typename Func, typename Variant>
using visitor_result_t = typename visitor_result<Func, Variant>::type;
template<typename Func, typename Variant>
visitor_result_t<Func, Variant> generic_visit(Func func, Variant variant)
{
return boost::apply_visitor([&](auto a) -> visitor_result_t<Func, Variant> { return func(a); }, variant);
}
现场观看
也使用变量结果类型。换句话说:
boost::variant<std::vector<int>, std::vector<double> > vr
= std::vector<int>(5, 5);;
boost::apply_visitor([](auto a) -> boost::variant<int, double> {
using T = std::decay_t<decltype(a)>;
if constexpr (std::is_same_v<T, std::vector<int>>) {
int v = 0;
for(auto q : a) v += q;
return v;
}
else if constexpr (std::is_same_v<T, std::vector<double>>) {
double v = 0;
for(auto q : a) v += q;
return v;
}
}, vr);
我可以想象通过自动推断正确的变体返回类型来完成此操作,但这需要大量编码。
基于@Caleth的答案,这允许任何变体,而无需复制参数列表。
#include <vector>
#include <variant>
#include <type_traits>
//Replace with more appropriate name
template<typename Variant, typename Lambda>
struct X_impl;
template<typename...Ts, typename Lambda>
struct X_impl<std::variant<Ts...>, Lambda>{
using type = std::variant<std::invoke_result_t<Lambda,Ts>...>;
};
template<typename...Ts, typename Lambda>
struct X_impl<const std::variant<Ts...>, Lambda>{
using type = std::variant<std::invoke_result_t<Lambda,const Ts>...>;
};
template<typename Variant, typename Lambda>
using X = typename X_impl<std::remove_reference_t<Variant>, Lambda>::type;
template<typename Variant, typename Lambda>
auto visit(Variant&& variant, Lambda&& lambda){
auto wrapped_lambda = [&lambda](auto&& arg) -> X<Variant,Lambda>{
using T = decltype(arg);
return std::forward<Lambda>(lambda)(std::forward<T>(arg));
};
return std::visit(wrapped_lambda, std::forward<Variant>(variant));
}
int main()
{
std::variant<std::vector<int>,const std::vector<double> > vr = std::vector<int>(5, 5);
const std::variant<std::vector<int>,const std::vector<double> > c_vr = std::vector<int>(5, 5);
auto& ref_vr = vr;
auto& ref_c_vr = c_vr;
auto visit_fnc = [](auto&& a){return a.begin();};
visit(vr, visit_fnc);
visit(c_vr, visit_fnc);
visit(ref_vr, visit_fnc);
visit(ref_c_vr, visit_fnc);
}
编辑:哦,看起来@Caleth同时还添加了通用解决方案。
我认为您需要以不同的方式做事,迭代器在您当前的情况下是不同的,但您可以绕过它:
std::vector<boost::variant<int, double>> vr = std::vector<int>(5, 5);
这基本上是一回事。
相关文章:
- 如何获取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++ 中将函数返回类型指定为模板参数
- 具有不同返回类型的变体访客
- 工厂返回提升::访客对象
- 提升::访客多次使用相同的数据类型
- 使用具有返回值的访客模式实现 AST 的最佳方法是什么?