And*_*owl 14 c++ polymorphism runtime boost-variant c++11
目标:
我想在不相关的类型上实现类型安全的动态多态(即函数调用的运行时调度)- 即在没有公共基类的类型上.在我看来,这是可以实现的,或者至少在理论上是合理的.我会尝试更正式地定义我的问题.
问题定义:
鉴于以下内容:
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, ...);,并满足以下约束:
fENABLE_CALL_ON_VARIANT(f)A1, ..., AnENABLE_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");
}
Run Code Online (Sandbox Code Playgroud)
该程序的输出应为: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); \
}();
Run Code Online (Sandbox Code Playgroud)
如果只允许在本地类中使用模板成员,那么哪种方法可以正常工作(请参阅此问题).有没有人知道如何解决这个问题,或者提出另一种方法?
一段时间过去了,C++ 14正在最终确定,编译器正在增加对新功能的支持,比如通用lambda.
通用lambda与下面显示的机制一起,允许用不相关的类实现所需的(动态)多态性:
#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; });
Run Code Online (Sandbox Code Playgroud)
让我们付诸实践.假设有以下两个不相关的类:
#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;
}
};
Run Code Online (Sandbox Code Playgroud)
可以通过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;
}
Run Code Online (Sandbox Code Playgroud)
正如预期的那样,输出是:
A::foo(42, 3.14, Hello)
1
B::foo(1337, 6.28, World)
2
Run Code Online (Sandbox Code Playgroud)
该计划已于2013年11月的CTP上在VC12上进行了测试.不幸的是,我不知道任何支持通用lambda的在线编译器,所以我不能发布一个实例.