Vit*_*meo 8 c++ lambda variant boost-hana c++17
我想使用lambdas和重载创建函数(例如)访问"递归" .std::variantboost::hana::overload
假设我有一个变量类型my_variant,可以存储一个a int,a float或a vector<my_variant>:
struct my_variant_wrapper;
using my_variant =
std::variant<int, float, std::vector<my_variant_wrapper>>;
struct my_variant_wrapper
{
my_variant _v;
};
Run Code Online (Sandbox Code Playgroud)
(我正在使用包装my_variant_wrapper类来递归地定义变体类型.)
我想以递归方式访问变体,根据存储的类型打印不同的东西.这是一个使用基于访问者的工作示例struct:
struct struct_visitor
{
void operator()(int x) const { std::cout << x << "i\n"; }
void operator()(float x) const { std::cout << x << "f\n"; }
void operator()(const std::vector<my_variant_wrapper>& x) const
{
for(const auto& y : x) std::visit(*this, y._v);
}
};
Run Code Online (Sandbox Code Playgroud)
std::visit使用上述访问者调用打印所需的输出:
my_variant v{
std::vector<my_variant_wrapper>{
my_variant_wrapper{45},
std::vector<my_variant_wrapper>{
my_variant_wrapper{1}, my_variant_wrapper{2}
},
my_variant_wrapper{33.f}
}
};
std::visit(struct_visitor{}, v);
// Prints:
/*
45i
1i
2i
33f
*/
Run Code Online (Sandbox Code Playgroud)
我想在本地创建访问者作为一系列使用boost::hana::overload和的重载lambdas boost::hana::fix.
fix是Y-combinator的一个实现,可用于在类型推导的lambdas中实现递归.(有关详细信息,请参阅此问题.)
这是我尝试过的,并且有望工作:
namespace bh = boost::hana;
auto lambda_visitor = bh::fix([](auto self, const auto& x)
{
bh::overload(
[](int y){ std::cout << y << "i\n"; },
[](float y){ std::cout << y << "f\n"; },
[&self](const std::vector<my_variant_wrapper>& y)
{
for(const auto& z : y) std::visit(self, z._v);
})(x);
});
Run Code Online (Sandbox Code Playgroud)
我的推理如下:
boost::hana::fix返回一个可以用作访问者的一元通用lambda std::variant.
boost::hana::fix 采用二进制泛型lambda,其中第一个参数是一个允许递归lambda的一元函数,第二个参数是lambda主体的初始参数.
boost::hana::overload使用处理程序调用内部所有可能的类型my_variant会创建某种类似于的访问者struct_visitor.
使用self而不是lambda_visitor在const std::vector<my_variant_wrapper>&重载内部应该允许递归正常工作.
立即调用创建的重载bh::overload(...)(x)应该触发递归访问.
不幸的是,正如你在这个wandbox示例中看到的那样,该lambda_visitor示例无法编译,喷出了许多几乎无法解释的模板错误:
...
/usr/local/boost-1.61.0/include/boost/hana/functional/fix.hpp:74:50:错误:使用'main():: [with auto:2 = boost :: hana :: fix_t >; auto:3 = int]'在扣除'auto'之前{return f(fix(f),static_cast(x)...); }
...
错误似乎与我没有使用的情况类似boost::hana::fix:
auto lambda_visitor = bh::overload(
[](int y){ std::cout << y << "i\n"; },
[](float y){ std::cout << y << "f\n"; },
[](const std::vector<my_variant_wrapper>& y)
{
for(const auto& z : y) std::visit(lambda_visitor, z._v);
});
std::visit(lambda_visitor, v);
Run Code Online (Sandbox Code Playgroud)
错误:在扣除'auto'之前使用'lambda_visitor'(const auto&z:y)std :: visit(lambda_visitor,z._v);
我究竟做错了什么?是否有可能实现本地递归variant探视使用fix,overload和一组lambda表达式?
我的直觉是,lambda_visitor本来"相当于"来struct_visitor,这要归功于所提供的间接fix.
让我们选择一个更简单的例子.我们希望gcd使用fix-point combinator 来实现.首先可能是这样的:
auto gcd = bh::fix([](auto self, int a, int b) {
return b == 0 ? a : self(b, a%b);
});
std::cout << gcd(12, 18);
Run Code Online (Sandbox Code Playgroud)
这无法使用gcc编译最终产生此错误:
/usr/local/boost-1.61.0/include/boost/hana/functional/fix.hpp:74:50: error: use of 'main()::<lambda(auto:2, int, int)> [with auto:2 = boost::hana::fix_t<main()::<lambda(auto:2, int, int)> >]' before deduction of 'auto'
{ return f(fix(f), static_cast<X&&>(x)...); }
^
Run Code Online (Sandbox Code Playgroud)
我们传递给的lambda fix()有一个推导出的返回类型.但是我们如何推断呢?只有一个return语句,那个是递归的!我们需要给编译器一些帮助.要么我们需要拆分我们的return陈述,以便有一个明确的类型:
auto gcd = bh::fix([](auto self, int a, int b) {
if (b == 0) {
return a;
}
else {
return self(b, a%b);
}
});
Run Code Online (Sandbox Code Playgroud)
或者只是明确地提供返回类型:
auto gcd = bh::fix([](auto self, int a, int b) -> int {
return b == 0 ? a : self(b, a%b);
});
Run Code Online (Sandbox Code Playgroud)
这两个选项都可以编译和工作.
您的原始示例也是如此.如果你只是指定lambda返回void,那么一切正常:
auto lambda_visitor = bh::fix([](auto self, const auto& x) -> void
// ^^^^^^^^
{
bh::overload(
[](int y){ std::cout << y << "i\n"; },
[](float y){ std::cout << y << "f\n"; },
[&self](const std::vector<my_variant_wrapper>& y)
{
for(const auto& z : y) std::visit(self, z._v);
})(x);
});
std::visit(lambda_visitor, v);
Run Code Online (Sandbox Code Playgroud)