gpt4 book ai didi

c++ - 使用 lambda 和定点组合器递归访问 `std::variant`

转载 作者:可可西里 更新时间:2023-11-01 17:44:32 27 4
gpt4 key购买 nike

我想访问一个“递归” std::variant使用 lambda 和重载创建函数(例如 boost::hana::overload)


假设我有一个名为 my_variant 的变体类型可以存储一个int , 一个 floatvector<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;
};

(我使用包装器 my_variant_wrapper 类来递归定义变体类型。)


我想递归访问根据存储类型打印不同内容的变体。这是一个 working example使用 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);
}
};

调用 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
*/

我想使用 boost::hana::overload 在本地将访问者创建为一系列重载的 lambda 表达式和 boost::hana::fix .

fixY-combinator 的一个实现,可用于在类型推导的 lambda 中实现递归。 (有关更多信息,请参阅 this question。)

这是我尝试过的,预计会奏效:

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

我的推理如下:

  • boost::hana::fix返回一个一元泛型 lambda,可用作 std::variant 的访问者.

  • boost::hana::fix采用二进制通用 lambda,其中第一个参数是允许 lambda 递归的一元函数,第二个参数是 lambda 主体的初始参数。

  • 调用 boost::hana::overloadmy_variant 中包含所有可能类型的处理程序创建某种访客,相当于 struct_visitor .

  • 使用 self而不是 lambda_visitorconst std::vector<my_variant_wrapper>&里面重载应该允许递归正常工作。

  • 立即使用 bh::overload(...)(x) 调用创建的重载应该触发递归访问。

不幸的是,as you can see in this wandbox example , lambda_visitor示例无法编译,喷出大量几乎无法辨认的模板错误:

...

/usr/local/boost-1.61.0/include/boost/hana/functional/fix.hpp:74:50: error: use of 'main():: [with auto:2 = boost::hana::fix_t >; auto:3 = int]' before deduction of '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);

error: use of 'lambda_visitor' before deduction of 'auto' for(const auto& z : y) std::visit(lambda_visitor, z._v);


我做错了什么?是否有可能使用 fix 实现本地递归变体访问 , overload和一组 lambda?

我的直觉是 lambda_visitor本来“等同于” struct_visitor , 感谢 fix 提供的间接寻址.

最佳答案

让我们选择一个更简单的例子。我们想使用定点组合子来实现 gcd。首先可能是这样的:

auto gcd = bh::fix([](auto self, int a, int b) {
return b == 0 ? a : self(b, a%b);
});

std::cout << gcd(12, 18);

这无法使用 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)...); }
^

我们传递给 fix() 的 lambda 具有推导的返回类型。但是我们如何推断呢?只有一个 return 语句,而且是递归的!我们需要给编译器一些帮助。要么我们需要分解我们的 return 语句,以便有一个清晰的类型:

auto gcd = bh::fix([](auto self, int a, int b) {
if (b == 0) {
return a;
}
else {
return self(b, a%b);
}
});

或者简单地明确提供返回类型:

auto gcd = bh::fix([](auto self, int a, int b) -> int {
return b == 0 ? a : self(b, a%b);
});

这两个选项都可以编译和工作。

您的原始示例也是如此。如果您只是指定 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);

关于c++ - 使用 lambda 和定点组合器递归访问 `std::variant`,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39819600/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com