【发布时间】:2018-09-26 19:54:20
【问题描述】:
在过去几年中,类型擦除或基于概念的运行时多态性变得非常流行,参见例如Sean Parent Better Code: Runtime Polymorphism、Inheritance Is The Base Class of Evil 或 C++ Seasoning 的会谈。 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 执行此操作。参数类型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 ArgType = MyClass,一切都很好。
(ii) 如果RType 或ArgType 是基于概念的多态类型,例如object_t,它也可以。 但这意味着代码库将充满接口类,例如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::draw根据给定的参数返回不同的值吗?您是否希望将每个T分配给object_t能够获取anyArgType,或者您是否希望每个T能够将特定ArgType映射到一个特定的ArgType具体RType?如果是后者,您希望object_t成为ArgType上的模板,还是希望object_t::draw强制包含的对象能够接受任何ArgType? -
您不能不使用 (ii) 或使用协变返回类型规则提供的灵活性。
-
@FrançoisAndrieux 抱歉,我不清楚。目前,任何版本都可以。
-
@NicolBolas 我编辑了这个问题。我希望这个问题现在已经清楚了。
标签: c++ type-erasure