[英]How to allow custom return types of member functions for type erasure in C++?
在过去的几年中, 类型擦除或基于概念的运行时多态性变得非常流行,例如,参见Sean Parent的 代码更好的代码:运行时多态性 , 继承是邪恶或C ++调味 的基类 。 在实现通过的Adobe , Facebook的 , Boost.TypeErasure , Boost.te或DYNO 。
以下是Sean Parent的演讲中的一个例子:
class object_t {
struct concept_t {
virtual ~concept_t() = default;
virtual void draw_(ostream&, size_t) const = 0;
};
template <typename T>
struct model final : concept_t {
model(T x) : data_(move(x)) { }
void draw_(ostream& out, size_t position) const override
{ draw(data_, out, position); }
T data_;
};
shared_ptr<const concept_t> self_;
public:
template <typename T>
object_t(T x) : self_(make_shared<model<T>>(move(x))){ }
friend void draw(const object_t& x, ostream& out, size_t position)
{ x.self_->draw_(out, position); }
};
在上面的例子中,任何类T
都必须提供一个函数
void draw(T data, std::ostream out, size_t position);
这对于任何T
都很容易实现,因为返回类型是void
而且参数签名都是编译时间。 如果返回类型是int,double,std :: string等,那也是如此。
以下是实际问题: 如何为自定义返回类型RType
resp执行此RType
。 参数类型ArgType
?
class object_t {
struct concept_t {
virtual ~concept_t() = default;
virtual void draw_(ostream&, size_t) const = 0;
virtual RType foo (ArgType arg) const = 0; // new function
};
template <typename T>
struct model final : concept_t {
model(T x) : data_(move(x)) { }
void draw_(ostream& out, size_t position) const override
{ draw(data_, out, position); }
RType foo (ArgType arg) const override
{ return data_.foo(arg);}; // new function
T data_;
};
shared_ptr<const concept_t> self_;
public:
template <typename T>
object_t(T x) : self_(make_shared<model<T>>(move(x))){ }
friend void draw(const object_t& x, ostream& out, size_t position)
{ x.self_->draw_(out, position); }
RType foo (ArgType arg) const
{ return self_->foo(arg);}; // new function
};
(i)如果RType
或ArgType
是显式类型,即存在类型class MyClass{};
我们可以using RType = MyClass;
定义using RType = MyClass;
或者using ArgType = MyClass
,一切都很好。
(ii)如果RType
或ArgType
是基于概念的多态类型,例如object_t
它也可以。
现在有问题的部分:如果我using RType = typename T::RType
或using ArgType = typename T::ArgType
作为类T
???,我可以使示例工作吗?
即
class T {
public:
using ArgType = /*...*/;
using RType = /*...*/;
/*...*/
};
编辑:
最终目标是有两个班级
class MyClass {
using ArgType = /* e.g. int*/;
using RType = /* e.g. double*/;
RType foo (ArgType arg) const {/*...*/;}
};
和
class YourClass {
using ArgType = /* e.g. size_t*/;
using RType = /* e.g. float*/;
RType foo (ArgType arg) const {/*...*/;}
};
这样我们就可以同时使用object_t
,即
object_t myObj(MyClass(/*...*/));
object_t yourObj(YourClass(/*...*/));
// use provided functionality
// ... myObj.foo(...)
// ... yourObj.foo(...)
当然, object_t::foo
任何一个重载都必须有一个返回类型,并且有充分的理由:如果客户端可以接收一个只存在于创建object_t
其他转换单元中的类型的object_t
,编译器怎么可能生成代码来处理? 如果有一些类型可以转换所有各种返回类型,你可以使用它作为外部返回类型,但这肯定是太有限。
当然,您可以提供至少接受任何类型的foo
函数模板 ,但是您将如何实现它? 功能模板不能是virtual
,因为类似的原因是从任何一个翻译单元都无法知道(典型)vtable的大小。 您可以将几种已知类型中的每一种路由到它自己的虚函数,但必须事先知道这些函数的列表; 或者,您可以再次依赖将任何参数转换为某种已知类型,但模板的客户端可以自己执行此操作。
唯一的另一种选择是为参数和返回值定义一个接口 ,以便为它提供和编写所有实现类和客户端代码。 当然,有多少脚本语言可以工作:实质上,CPython中的每个内置函数都有签名
PyObject* func(PyObject*);
因此,即使是有状态的C ++可访问的任意Python数据操纵器也可以存储在std::function<PyObject*(PyObject*)>
而无需定义自己的类型擦除类。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.