目标:
我想在不相关的类型上实现类型安全的动态多态性(即函数调用的运行时调度)在不相关的类型上 - 即在执行的类型上没有共同的基类。在我看来,这是可以实现的,或者至少在理论上是合理的。我会尝试更正式地定义我的问题。
问题定义:
鉴于以下情况:
- 两个或多个不相关类型
A1, ..., An
, 每个都有一个名为f
的方法,可能具有不同的签名,但具有相同的返回类型R
;和 - 一个
boost::variant<A1*, ..., An*>
对象v
(或任何其他类型的变体)可以且必须在任何时候采用任何这些类型的一个值;
我的目标是编写概念上等同于 v.f(arg_1, ..., arg_m);
的指令这将在运行时调度以运行Ai::f
如果v
中包含的值的实际类型是 Ai
.如果调用参数与each函数的形参不兼容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, ...)
)。命名 return 类型是可以的,例如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); \
}();
如果本地类中只允许使用模板成员,那将非常有效(参见 this question)。有没有人知道如何解决这个问题,或建议一种替代方法?
最佳答案
一段时间过去了,C++14 即将完成,编译器正在添加对新功能的支持,例如通用 lambda。
通用 lambdas 与下面显示的机制一起,允许使用不相关的类实现所需的(动态)多态性:
#include <boost/variant.hpp>
template<typename R, typename F>
class delegating_visitor : public boost::static_visitor<R>
{
public:
delegating_visitor(F&& f) : _f(std::forward<F>(f)) { }
template<typename T>
R operator () (T x) { return _f(x); }
private:
F _f;
};
template<typename R, typename F>
auto make_visitor(F&& f)
{
using visitor_type = delegating_visitor<R, std::remove_reference_t<F>>;
return visitor_type(std::forward<F>(f));
}
template<typename R, typename V, typename F>
auto vcall(V&& vt, F&& f)
{
auto v = make_visitor<R>(std::forward<F>(f));
return vt.apply_visitor(v);
}
#define call_on_variant(val, fxn_expr) \
vcall<int>(val, [] (auto x) { return x-> fxn_expr; });
让我们把它付诸实践。假设有以下两个不相关的类:
#include <iostream>
#include <string>
struct A
{
int foo(int i, double d, std::string s) const
{
std::cout << "A::foo(" << i << ", " << d << ", " << s << ")";
return 1;
}
};
struct B
{
int foo(int i, double d, std::string s) const
{
std::cout << "B::foo(" << i << ", " << d << ", " << s << ")";
return 2;
}
};
可以通过这种方式多态地调用 foo()
:
int main()
{
A a;
B b;
boost::variant<A*, B*> v = &a;
auto res1 = call_on_variant(v, foo(42, 3.14, "Hello"));
std::cout << std::endl<< res1 << std::endl;
v = &b;
auto res2 = call_on_variant(v, foo(1337, 6.28, "World"));
std::cout << std::endl<< res2 << std::endl;
}
输出如预期的那样:
A::foo(42, 3.14, Hello)
1
B::foo(1337, 6.28, World)
2
该程序已在 VC12 上使用 2013 年 11 月的 CTP 进行了测试。不幸的是,我不知道任何支持通用 lambda 的在线编译器,所以我无法发布一个实时示例。
关于c++ - 如何在不相关的类型上实现动态多态性(运行时调用调度)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14288050/