【问题标题】:Template return type of a function that needs to be virtual需要为虚函数的模板返回类型
【发布时间】:2015-07-07 03:56:01
【问题描述】:

因为以下是非法的:

struct A {
    template <typename T>
    virtual T* foo() = 0;
};

struct B : A {
    template <typename T>
    virtual T* foo() override {return new T;}  // Simple example here.
};

template <typename T>
T* bar (A* a) {
    return a->foo<T>();  // The need for the virtual method.
}

并且模板只出现在返回类型中,我想到了一个(天真的?)使用重载的解决方法:

#include <iostream>

struct Base { virtual void show() const = 0; };
struct Object : Base { virtual void show() const override {std::cout << "I am an Object.\n";} };
struct Thing : Base { virtual void show() const override {std::cout << "I am a Thing.\n";} };
struct Blob : Base { virtual void show() const override {std::cout << "I am a Blob.\n";} };

struct A {
    virtual Object* foo (Object&&) = 0;
    virtual Thing* foo (Thing&&) = 0;
    virtual Blob* foo (Blob&&) = 0;
};

struct B : A {
    virtual Object* foo (Object&&) override {return fooHelper<Object>();}
    virtual Thing* foo (Thing&&) override {return fooHelper<Thing>();}
    virtual Blob* foo (Blob&&) override {return fooHelper<Blob>();}
private:
    template <typename T>
    T* fooHelper() {return new T;}  // Simple example here.
};

template <typename T>
T* bar (A* a) {
    return a->foo(T{});
}

int main() {
    B* b = new B;
    Base* list[] = {bar<Object>(b), bar<Thing>(b), bar<Blob>(b)};
    for (const Base* x : list) x->show();
}

这个方案的问题在于,只有在T的类型不多的情况下才可行。但如果有呢?此外,现在T 的新类型在以后引入时存在维护问题。

有人能想出比这更好的解决方案吗?已知的访问者模式作为虚拟模板解决方法在这里不适用(我不认为),因为模板没有出现在参数中。

【问题讨论】:

  • 你需要协变返回类型吗?
  • @marom 我的T* create (A* a) 功能是我的目标。我仅将协变返回类型用作解决方案,而不是目标本身。如果有另一种没有协变返回类型的方法,那就太好了。
  • 您的示例中有很多内存泄漏。
  • 您是否需要您的函数参数 (a) 成为类层次结构的一部分?否则它的类型可能只是另一个模板参数。
  • 你的目标是什么?让A 尽可能简单,比如只返回Base 并且可能有一个foo 函数? A 的子类有多少,它们都必须支持从Base 子类化的所有类型吗?编译时类型安全和A 接口的强制执行对你来说是绝对必要的还是你愿意做出妥协?

标签: c++ templates virtual


【解决方案1】:
...
    T* fooHelper() {return new T;}
};

template <typename T>
T* create (A* a) {
    return a->foo(T{});
}

A 的实例对您返回的 T 没有影响。我假设也许这应该是一个论点。看起来你也想要一个工厂。使用成员函数怎么样:

template < typename T, typename FactoryType, 
           typename MemFnType, typename ArgType >
T* create(FactoryType* f, MemFnType mfn, ArgType a)
{
    return (f->*mfn)(a);
}

完整示例:

#include <iostream>

struct Base { virtual void show() const = 0; };
struct Object : Base { virtual void show() const override {std::cout << "I am an Object.\n";} };
struct Thing : Base { virtual void show() const override {std::cout << "I am a Thing.\n";} };
struct Blob : Base { virtual void show() const override {std::cout << "I am a Blob.\n";} };

struct Args
{
    int someArg;
};

struct Factory
{
    // normally 'a' would be passed to the Object constructor.
    // omitted to save edits.
    Object* asObject(const Args& a) { return new Object(); }
    Thing* asThing(const Args& a) { return new Thing(); }
    Blob* asBlob(const Args& a) { return new Blob(); }
};

template < typename T, typename FactoryType, 
           typename MemFnType, typename ArgType >
T* create(FactoryType& f, MemFnType mfn, ArgType& a)
{
    return (&f->*mfn)(a);
}

int main() {
    Args arg;
    Factory f;
    Base* list[] = {create<Object>(f, &Factory::asObject, arg), create<Thing>(f, &Factory::asThing, arg), create<Blob>(f, &Factory::asBlob, arg)};
    for (const Base* x : list) x->show();
}

添加可构造类型只需要添加类型本身和相关的工厂函数。您甚至可以将其概括为完整的模板参数列表,而不是单个参数类型。

【讨论】:

  • 这不是我追求的工厂功能。我只是随机命名了函数create,而我刚刚在这里编写的函数体表明需要Tafoo,但返回了T*(但不是工厂函数)。我很感激你试图提供帮助。但我正在寻求改进我的解决方案而不进行任何重新设计。
  • 那么您的意思是B 包含指向ThingBlob 等的指针,或者有其他方法可以查找它们,但并不总是重新创建它们?
  • @ mat69 B::fooHelper() 返回一个 T* 但不一定创建一个。 C::fooHelper() 以不同的方式返回 T*,等等...
  • @prestokeys 所以A 的子类的目的之一(或者更确切地说是目的?)是提供不同的方法来检索子类Base 的对象?这是否意味着A 的任何子类都有一个fooHelper 具有相同的接口,但内容不同?
  • @mat69 是的,你是对的。虽然,我并不真正追求这里的工厂模式,但我将尝试重新设计类似于您的解决方案,创建一个新类来管理我要管理的内容,并使用模板来代替。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-06-21
  • 1970-01-01
  • 1970-01-01
  • 2018-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多