【问题标题】:How to create abstract class until Derived Instantiation在派生实例化之前如何创建抽象类
【发布时间】:2014-04-17 04:34:32
【问题描述】:

如何创建派生类并在创建派生类时实例化基类。

这样

template<class T> 
struct Base 
{
  typedef T type;
  static const int n = 3;
  virtual int f() = 0;
  int f(int x) { return x * 2; }
};

// doesn't compile!
template<class T> 
struct Derived : Base<T> 
{
    type field;         // The compiler doesn't know Base<T>::type yet!
    int f() { return n; } // the compiler doesn't know n yet, and f(int) is maksed!
};

【问题讨论】:

    标签: c++ inheritance metaprogramming


    【解决方案1】:

    您可以通过多种方式做到这一点,如果您希望派生类自然地使用基类,您可以尝试使用模板模板。

    假设你有相同的基类,那么派生类应该是这样的:

    template< class T, template<typename> class Base > 
    struct Derived : Base<T> 
    {
        typename Base<T>::type field; 
        int f() 
        {
          return n;
        }
        int f(int x)
        {
          return Base<T>::f(x);
        }
    };
    

    但是要实例化 Derived 类,您还需要指定 Base 类。

    这样

    Derived<int, Base> object; 
    

    现在假设您不想这样做,您只想从 SAME 基类实例化 Derived 对象,那么您必须专门化派生模板。 p>

    template< class T, template<typename> class Base = Base > 
    struct Derived : Base<T> 
    {
        Base<T>::type field; 
        int f() 
        {
           return n;
        }
        int f(int x)
        {
            return Base<T>::f(x);
        }
    };
    

    这样你现在可以创建Derived&lt;int&gt;Derived&lt;string&gt;

    喜欢这个

    int main()
    {
        Derived<int> bs1;
        bs1.field = 200;
        std::cout << bs1.f() << std::endl;
        std::cout << bs1.f(50)<< std::endl;
        std::cout << bs1.field<< std::endl;
    
        Derived<std::string> bs2;
        bs2.field = "example";
        std::cout << bs2.f() << std::endl;
        std::cout << bs2.f(50)<< std::endl;
        std::cout << bs2.field<< std::endl;
    
        return 0;
    }
    

    什么时候应该这样做?

    当您出于某种原因打算切换基类时,可能是针对测试用例。更改基类也可以更改上下文。

    您还可以创建一个模板 typedef,它允许您将基本属性带入您自己的派生类。这是一个很好的方法。

    【讨论】:

    • 你能解释一下为什么这种复杂性是必要的吗?
    • 一切都取决于您的应用程序。真正的问题是为什么当它可能是其他 wau 时,实现应该依赖于接口。另一件事,如果您将这种复杂性称为复杂性,那么显然您需要继续深入研究设计模式。
    • 我并没有把这种复杂性称为绝对意义上的复杂性——我经常使用 Boost.Preprocessor 和模板元编程 :-) 但我不明白你为什么要为一些没有的东西引入模板参数需要它。您声称“要成功继承模板类,有必要指定您是从模板继承的。...派生必须看起来像这样:”这些完全是不正确的陈述。
    • 您对此绝对正确,我将编辑我的答案以获得更正确的解释。
    【解决方案2】:

    您可以通过using 声明引入相关名称:

    template<class T> 
    struct Base 
    {
      typedef T type;
      static const int n = 3;
      virtual int f() = 0;
      int f(int x) { return x * 2; }
    };
    
    template<class T> 
    struct Derived : Base<T> 
    {
        using typename Base<T>::type;
        using Base<T>::n;
    
        type field;
        int f() { return n; }
    };
    

    Live example

    对于以这种方式继承的类型,您还必须使用typename 关键字(就像我在上面所做的那样),以便编译器知道名称指的是一个类型。有一个 question on SO 提供有关此要求的更多信息。

    另一种方法是明确限定名称:使用typename Base&lt;T&gt;::type 代替typeBase&lt;T&gt;::n 代替n。您选择哪种很大程度上取决于偏好。

    【讨论】:

      【解决方案3】:

      在为某些类型 T 实例化 Derived 之前,编译器无法猜测 typen 应该来自哪里。毕竟,在定义Derived 之后 为不同类型提供Base 的特化是合法的。

      你需要改变的是:

      template<class T> 
      struct Derived : Base<T> 
      {
          typename Base<T>::type field; // tell the compiler it's a type,
                                        // and where it comes from
          int f() { return Base<T>::n; }// tell the compiler where n comes from too
      };
      

      using 声明的工作方式相同,通过限定这些名称,如果您多次使用每个名称,它最终可能看起来更简洁。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-05-03
        • 2017-04-28
        • 2015-03-23
        • 2023-03-04
        • 2014-10-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多