【问题标题】:C++ templates with interface objects带有接口对象的 C++ 模板
【发布时间】:2011-11-03 07:02:21
【问题描述】:

我有以下无法编译的代码。

class Base {
    public:
        virtual ~Base() { };
};

class Derived : public Base { };

class NotDerived { };

template <typename T>
class Group { };

int main() {
    Group<Base> *g = NULL;

    g = new Group<Base>();       // Works
    g = new Group<Derived>();    // Error, but I want it to work

    g = new Group<NotDerived>(); // Error, as expected
}

我知道这不会编译,因为g 是与Group&lt;Derived&gt; 不同的类型。为了在 Java 中完成这项工作,我会做一些事情,例如Group&lt;? extends Base&gt; g,但据我所知,C++ 没有那个关键字。可以做什么?

编辑:我想澄清一下,我不希望将不是从Base 派生的类型设置为g。我更新了我的示例来解释这一点。

编辑 2:我的问题有两种解决方案。 Dave's 我发现它很简单,很容易定义。但是Bowie's(以及Mark's 的补充)更适合我的需求。

【问题讨论】:

  • 我不熟悉这种东西在 Java 中是如何工作的。你想用这种语法表达什么?
  • 这在 Java 中也不起作用。 &lt;? extends ...&gt; 的语法不是为了支持协方差,而是对泛型可以使用的类型添加约束。
  • Nicol:这样我就可以拥有一种返回某种类型接口的工厂类。马克:它确实可以在 Java 中工作。 Praetorian:这似乎正是我的问题,谢谢!

标签: c++ templates inheritance


【解决方案1】:

Group&lt;Base&gt;Group&lt;Derived&gt; 类是完全不相关的不同类。指向它们的指针在任一方向都不可转换。

如果您需要运行时多态行为,您的类模板 Group 可以派生自一个通用(非模板化)基类:

class Group // base
{
   virtual ~Group() { }
};

template <typename T>
class ConcreteGroup : public Group
{
  // ...
  T * m_impl;
};

Group * g1 = new ConcreteGroup<A>;
Group * g1 = new ConcreteGroup<B>;

【讨论】:

  • ConcreteGroup 不需要继承Group?
【解决方案2】:

您可以使 Group 成为所有 GroupT != Base 的基类。

class Base {
    public:
        virtual ~Base() { };
};

class Derived : public Base { };


template <typename T> class Group;

struct Empty { };

template <typename T>
struct base_for_group_t {
    typedef Group<Base> type;
};

template <>
struct base_for_group_t<Base> {
    typedef Empty type;
};

template <typename T>
class Group : public base_for_group_t<T>::type { };

int main() {
    Group<Base> *g = 0;

    g = new Group<Base>();    // Works
    g = new Group<Derived>(); //  now works
}

【讨论】:

  • 不幸的是,现在这样的事情是合法的:class NotDerived {}; g = new Group&lt;NotDerived&gt;();。如果可能,我想保持类型安全。
【解决方案3】:

Bowie Owens's Answer 处理解决原始问题所需的协方差。至于您在已编辑问题中要求的约束 - 您可以通过使用类型特征来实现这一点。

template <typename T, class Enable = void> class Group;

template <typename T>
class Group<T, typename enable_if<is_base_of<Base, T>::value>::type> 
    : public base_for_group_t<T>::type { };

【讨论】:

  • 不幸的是,我无法让它最终编译(我不熟悉如何正确使用类型特征)。你能提供一个更完整的例子吗? (我假设Group&lt;T, typename ...&gt; 应该是template&lt;typename T, typename enable_if...&gt; class Group,但是这不起作用。
  • 使用 is_base_of 上的 ::value 重试。 (boost::enable_ifstd::enable_if 之间需要区别)。如果没有,你得到的错误是什么?
  • 啊,我没有包含 std 命名空间。您的原始代码按预期工作。 ;)
  • 你应该接受鲍伊的回答而不是我的回答,他做得很聪明。
【解决方案4】:

我认为 C++ 不支持这一点。 C++ 模板在编译时完全处理,因此它不支持多态性。这意味着赋值表达式两边的模板参数的类型应该完全相同。

【讨论】:

    【解决方案5】:

    我想我明白你的目的。我不确定这是不是最好的方法(你可能想看看Boost.Factory)。

    template <class T>
    class Factory {
    
    public:
      virtual T* operator()() = 0;
    };
    
    template <class Derived, class Base>
    class ConcreteFactory : public Factory<Base> {
    
    public:
      virtual Base* operator()() {
        return new Derived();
      }
    };
    
    class Base {
    public:
      virtual ~Base() {};
    };
    
    class Derived1 : public Base { };
    class Derived2: public Base {};
    
    class NotDerived {};
    
    int main()
    {
      Factory<Base>* g;
    
      g = new ConcreteFactory<Derived1, Base>;
      g = new ConcreteFactory<Derived2, Base>;
      //  g = new ConcreteFactory<NotDerived, Base>; // Will not work if you try to do this
    }
    

    【讨论】:

    • 这似乎是迄今为止最安全的解决方案。 Derived1 和 Base 的定义有点多余,但我已经不知道了(我可以使用 typedef 来避免)。谢谢,:)
    猜你喜欢
    • 2012-08-30
    • 1970-01-01
    • 2012-02-09
    • 1970-01-01
    • 1970-01-01
    • 2018-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多