目标:
我想在不相关的类型上实现类型安全的动态多态性(即函数调用的运行时分派)- 即在没有公共基类的类型上。在我看来,这是可以实现的,或者至少在理论上是合理的。我将尝试更正式地定义我的问题。
问题定义:
鉴于以下情况:
- 两个或多个不相关的类型
A1, ..., An
,每个类型都有一个名为 的方法f
,可能具有不同的签名,但具有相同的返回类型R
;和 - 一个
boost::variant<A1*, ..., An*>
对象v
(或任何其他类型的变体),它可以而且必须在任何时候假设任何这些类型的一个值;
我的目标是编写在概念上等同于v.f(arg_1, ..., arg_m);
将在运行时调度以运行的指令,Ai::f
如果其中包含的值的实际类型v
是Ai
. 如果调用参数与每个函数的形式参数不兼容Ai
,编译器应该引发错误。
当然我不需要拘泥于语法v.f(arg_1, ..., arg_m)
:例如,类似的东西call(v, f, ...)
也是可以接受的。
我试图在 C++ 中实现这一点,但到目前为止我还没有想出一个好的解决方案(我确实有很多不好的解决方案)。下面我澄清一下我所说的“好的解决方案”是什么意思。
约束:
一个好的解决方案是任何让我模仿v.f(...)
成语的东西,例如call_on_variant(v, f, ...);
,并满足以下约束:
- 不需要为必须以这种方式调用的每个函数(例如)或任何可以在代码中的其他地方以多态方式处理的不相关类型列表(例如)进行任何类型的单独声明,尤其是在全局范围内;
f
ENABLE_CALL_ON_VARIANT(f)
A1, ..., An
ENABLE_VARIANT_CALL(A1, ..., An)
- 调用时不需要显式命名输入参数的类型(例如
call_on_variant<int, double, string>(v, f, ...)
)。命名返回类型是可以的,所以例如call_on_variant<void>(v, f, ...)
是可以接受的。
遵循一个示范性的例子,希望能阐明我的愿望和要求。
例子:
struct A1 { void f(int, double, string) { cout << "A"; } };
struct A2 { void f(int, double, string) { cout << "B"; } };
struct A3 { void f(int, double, string) { cout << "C"; } };
using V = boost::variant<A1, A2, A3>;
// Do not want anything like the following here:
// ENABLE_VARIANT_CALL(foo, <whatever>)
int main()
{
A a;
B b;
C c;
V v = &a;
call_on_variant(v, f, 42, 3.14, "hello");
// Do not want anything like the following here:
// call_on_variant<int, double, string>(v, f, 42, 3.14, "hello");
V v = &b;
call_on_variant(v, f, 42, 3.14, "hello");
V v = &c;
call_on_variant(v, f, 42, 3.14, "hello");
}
这个程序的输出应该是:ABC
.
最佳(失败)尝试:
我最接近所需解决方案的是这个宏:
#define call_on_variant(R, v, f, ...) \
[&] () -> R { \
struct caller : public boost::static_visitor<void> \
{ \
template<typename T> \
R operator () (T* pObj) \
{ \
pObj->f(__VA_ARGS__); \
} \
}; \
caller c; \
return v.apply_visitor(c); \
}();
如果本地类中只允许模板成员(请参阅此问题),这将完美地工作。有没有人知道如何解决这个问题,或者提出一种替代方法?