我有两个结构
A
和B
共享相同的接口foo
我希望能够根据上下文使用 A
或 B
,因此使用 std::variant
似乎在这里很有趣。
结构
C
封装了变体并提供了foo
的实现,隐藏了对std::visit
的调用。
然后,最终用户可以像使用 C
或 A
一样使用 B
(参见示例函数 dosomething
)
#include <variant>
#include <cstdio>
struct A { void foo() { printf ("[A::foo]\n"); } };
struct B { void foo() { printf ("[B::foo]\n"); } };
struct C
{
C() {};
template<typename T> C (T&& t) : v(t) {}
void foo() { std::visit ([](auto&& arg) { arg.foo(); }, v); }
std::variant<A,B> v;
};
void dosomething (C c1)
{
C c2;
c1.foo(); // acts like a B
c2.foo(); // acts like a A
}
int main(int argc, char **argv)
{
dosomething (B());
}
显然,我可以轻松地将
C
定义为具有虚拟方法 foo
的接口,然后使 A
和 B
继承自 C
但我特别想避免由于动态多态性而潜在的过载
因为实际的 foo
可能非常简单(例如只是一个补充)并且经常被调用。
问题 1 这种设计是否确保可以在编译时知道使用
dosomething
对象对 B
的调用是
实际调用 B::foo
?换句话说,由于 std::variant
,我们仍然有一个(可能成本高昂的)间接吗?
问题 2 避免出于性能考虑的动态多态性的最佳替代方案是什么?我了解静态多态性,但最终,人们必须处理 使用像
Base<A>
和 Base<B>
这样的模板类,我需要最终用户只看到一个类,就像我的示例中的 struct C
一样。
更新请注意,在我的上下文中,
dosomething
的原型无法更改,特别是无法模板化,这使得设计变得更加困难。
使用模板:
template <class C>
void dosomething (C& c) {
c.foo();
}
如果您使用 C++20,您也可以使用概念来执行契约:
#include <cstdio>
template <typename T>
concept Fooable = requires(T t) {
t.foo();
};
struct A {
void foo() { std::puts("A"); }
};
struct B {
void foo() { std::puts("B"); }
};
template <Fooable C>
void dosomething(C& c) {
c.foo();
}
int main() {
A a;
B b;
dosomething(a);
dosomething(b);
}