- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我最近一直在尝试 C++17 的 std::variant
和 std::visit
,我发现它非常强大。我特别喜欢在多个变体对象上创建访问者模式的功能。这是我的意思的一个例子:
std::variant<int, float, char> v1 { 's' };
std::variant<int, float, char> v2 { 10 };
std::visit(overloaded{
[](int a, int b) { },
[](int a, float b) { },
[](int a, char b) { },
[](float a, int b) { },
[](auto a, auto b) { }, // << default!
}, v1, v2);
参见 https://www.bfilipek.com/2018/09/visit-variants.html了解详情。鉴于此,我想知道是否可以基于多态类型而不是变体对象编写类似的代码。
想一想我们使用动态多态性和父对象来编写通用接口(interface)的场景。然后我们想要实现依赖于几种多态类型的特定功能,即像
void fun(IFoo* ptr_foo, IBar* ptr_bar) {
{
Foo1* child_foo = dynamic_cast<Foo1*>(ptr_foo);
Bar1* child_bar = dynamic_cast<Bar1*>(ptr_bar);
if(child_foo && child_bar) { return fun(child_foo, child_bar) }
}
// ... other cases
{
Foo1* child_foo = dynamic_cast<Foo1*>(ptr_foo);
BarN* child_bar = dynamic_cast<BarN*>(ptr_bar);
if(child_foo && child_bar) { return fun(child_foo, child_bar) }
}
// ... other cases
{
FooN* child_foo = dynamic_cast<FooN*>(ptr_foo);
BarN* child_bar = dynamic_cast<BarN*>(ptr_bar);
if(child_foo && child_bar) { return fun(child_foo, child_bar) }
}
throw std::runtime_error{};
}
我知道以上内容远非最佳,但只是试图让场景尽可能清晰。
在这种情况下,为 fun
使用虚函数看起来并不简单,因为它取决于两个输入的类型。此外,我们正在努力避免这些功能的虚拟方法,因为我们更喜欢保留 IFoo
的界面或 IBar
对这些外部函数不可知。
对于访问函数的多个输入对象,使用访问者模式似乎也不合理。
最简单的方法似乎是使用 dynamic_cast
的示例实现我在上面展示了,但是当我们从 1 个输入到 N 个输入时,这会迅速增加要写入的案例数量。std::variant
+ std::visit
不过,上面的方法非常清晰直接地涵盖了这种情况。
总而言之,我们的约束/要求是:
dynamic_cast
没问题)std::visit
方法这有可能吗?
我正在考虑编写一个可变递归模板函数,类似于 std::visit
,这将自动生成所有类型的案例来检查。使用示例如下: visitPolymorphic<tuple<Foo1, Foo2>,tuple<Bar1, Bar2, Bar3>>(ptr_foo, ptr_bar)
这将 if-else 处理不同的模板输入类型,并调度正确的调用。
对此有什么想法吗?
最佳答案
你也可以在那里使用 std::variant
:
struct Foo1;
struct Foo2;
struct Foo3;
using FooVariant = std::variant<Foo1*, Foo2*, Foo3*>;
struct IFoo
{
virtual ~IFoo() = default;
FooVariant AsVariant() = 0;
// ...
};
struct Foo1 : IFoo
{
FooVariant AsVariant() override { return this;}
// ...
};
// Same for FooX
struct Bar1;
struct Bar2;
struct Bar3;
using BarVariant = std::variant<Bar1*, Bar2*, Bar3*>;
struct IBar
{
virtual ~IBar() = default;
BarVariant AsVariant() = 0;
// ...
};
struct Bar1 : IBar
{
BarVariant AsVariant() override { return this;}
// ...
};
// Same for BarX
然后
void fun(IFoo& foo, IBar& bar) {
std::visit(overloaded{
[](Foo1* a, Bar1* b) { /*..*/ },
[](Foo2* a, Bar2* b) { /*..*/ },
[](Foo3* a, auto* b) { /*..*/ },
[](auto* a, auto* b) { /*..*/ }, // << default!
},
foo.AsVariant(), bar.AsVariant()
);
}
如果你不想在接口(interface)中使用虚拟 AsVariant()
(但使用 dynamic_cast
),你可能仍然有自由函数:
FooVariant AsVariant(IFoo& foo)
{
if (auto* p = dynamic_cast<Foo1*>(&foo)) {
return p;
}
if (auto* p = dynamic_cast<Foo2*>(&foo)) {
return p;
}
if (auto* p = dynamic_cast<Foo3*>(&foo)) {
return p;
}
throw std::runtime_error("Invalid type");
}
关于c++ - 用于访问多态类型的 std::visit-like 函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57598253/
我来自 Asp.Net 世界,试图理解 Angular State 的含义。 什么是 Angular 状态?它类似于Asp.Net中的ascx组件吗?是子页面吗?它类似于工作流程状态吗? 我听到很多人
我一直在寻找 3 态拨动开关,但运气不佳。 基本上我需要一个具有以下状态的开关: |开 |不适用 |关 | slider 默认从中间开始,一旦用户向左或向右滑动,就无法回到N/A(未回答)状态。 有人
我是一名优秀的程序员,十分优秀!