繁体   English   中英

如何在C ++中允许自定义返回类型的成员函数进行类型擦除?

[英]How to allow custom return types of member functions for type erasure in C++?

在过去的几年中, 类型擦除基于概念的运行时多态性变得非常流行,例如,参见Sean Parent的 代码更好的代码:运行时多态性继承是邪恶C ++调味 的基类 在实现通过的AdobeFacebook的Boost.TypeErasureBoost.teDYNO

以下是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)如果RTypeArgType是显式类型,即存在类型class MyClass{}; 我们可以using RType = MyClass;定义using RType = MyClass; 或者using ArgType = MyClass ,一切都很好。

(ii)如果RTypeArgType是基于概念的多态类型,例如object_t它也可以。

现在有问题的部分:如果我using RType = typename T::RTypeusing 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM