【问题标题】:Downcasting from base pointer to templated derived types从基指针向下转换为模板派生类型
【发布时间】:2012-03-22 13:59:59
【问题描述】:

我有以下层次结构:

class base
{
public:
   virtual ~base(){}
   virtual void foo() {}
};

template <typename T>
class derived1 : public base
{
   virtual void foo() {};
};

template <typename T>
class derived2 : public base
{
   virtual void foo() {};
};

现在给定一个指向 base 的指针,我想知道是否 底层证券是派生1 或派生2。问题是 派生1和派生2都可以专门用于许多不同的 类型,使用 dynamic_cast 测试向下转换需要 要知道的模板类型。我最终得到了一些混乱、不可维护和不完整的代码:

base* b = new derived1<int>();

if (dynamic_cast<derived1<int>*> ||
    dynamic_cast<derived1<unsigned int>*> ||
    dynamic_cast<derived1<double>*>)
  std::cout << "is derived1";
else if (dynamic_cast<derived2<int>*> ||
    dynamic_cast<derived2<unsigned int>*> ||
    dynamic_cast<derived2<double>*>)
  std::cout << "is derived2";

有没有更好的方法,可以处理任何类型的特化?

【问题讨论】:

  • 一旦您知道类型是派生模板之一的特化,您打算做什么?
  • @James:为每个函数调用一个特定的函数 - 顺便说一句,还有更多的derived1和derived2
  • 需要动态测试基类指针的具体类型的条件代码是一种不好的代码气味。可能有一种方法可以更改您的设计以避免这种情况。
  • @Emile:这到底有什么不好?
  • @XanderTulip:好的,这是一个问题。如果您要为某个函数提供base,为什么它需要 调用仅在derived1 类上的函数?该函数要么采用derived1,要么采用base。如果您发现自己不得不这样做,您需要重新评估您的类层次结构设计。简而言之,你让自己陷入了一个糟糕的设计境地,你需要找到一种方法来摆脱它。为了帮助您做到这一点,我们需要知道您的代码实际上想要完成什么。

标签: c++ inheritance metaprogramming dynamic-cast downcast


【解决方案1】:

将依赖于类型的逻辑移入类型中。

代替:

if (dynamic_cast<derived1<int>*>(b) ||
    dynamic_cast<derived1<unsigned int>*>(b) ||
    dynamic_cast<derived1<double>*>(b))
  std::cout << "is derived1";
else if (dynamic_cast<derived2<int>*>(b) ||
    dynamic_cast<derived2<unsigned int>*>(b) ||
    dynamic_cast<derived2<double>*>(b))
  std::cout << "is derived2";

将virtual print_name() const 函数添加到base,然后执行:

void example() {
    std::unique_ptr<base> b(new derived1<int>());
    b->print_name();
}
class base
{
public:
   ~base(){}
   virtual void foo() {}
   virtual void print_name() const = 0;
};

template <typename T>
class derived1 : public base
{
   virtual void foo() {}
   virtual void print_name() const {
       std::cout << "is derived1";
   }
};

template <typename T>
class derived2 : public base
{
   virtual void foo() {}
   virtual void print_name() const {
       std::cout << "is derived2";
   }
};

【讨论】:

    【解决方案2】:

    在base 和derived1 或derived2 之间插入一个非模板类:

    class base
    {
    public:
       virtual ~base() {}  // **NOTE** Should be virtual
       virtual void foo() {}
    };
    
    class derived1_base : public base
    {
    };
    
    template <typename T>
    class derived1 : public derived1_base
    {
    public:
       virtual void foo() {}
    };
    
    class derived2_base : public base
    {
    };
    
    template <typename T>
    class derived2 : public derived2_base
    {
    public:
       virtual void foo() {}
    };
    

    在评论中,您提到:

    [我想] 为每个函数调用一个特定的函数 - 顺便说一句,不止 派生1和派生2

    将那个(虚拟)函数添加到derived1_base,你甚至不需要知道T了。

    if (dynamic_cast<derived1_base*>(foo))
    {
      std::cout << "is derived1";
      dynamic_cast<derived1_base*>(foo)->specific_derived1_function();
    }
    else if (dynamic_cast<derived2_base*>(foo))
    {
      std::cout << "is derived2";
      dynamic_cast<derived2_base*>(foo)->specific_derived2_function();
    }
    

    注意:我认为dynamic_cast&lt;&gt; 列表是一种代码气味,我敦促您重新考虑您的方法。

    【讨论】:

      【解决方案3】:

      您可以添加一个虚拟方法来进行某种元类型检查:

      class base
      {
      public:
          ~base(){}
          virtual void foo() {}
          virtual bool isa(const char* type_to_test){
                return strcmp(type_to_test,"base")==0;}
      };
      
      template <typename T>
      class derived1 : public base
      {
         virtual void foo() {};
         virtual bool isa(const char* type_to_test){
         return strcmp(type_to_test,"derived1")==0;}
      };
      

      【讨论】:

        【解决方案4】:

        方案一:再添加一个虚函数:

        enum DerivedType
        {
            One,
            Two,
            ...
        };
        
        class base 
        { 
        public: 
           ~base(){} 
           virtual void foo() {}
           virtual DerivedType GetType() = 0;
        }; 
        
        template <typename T> 
        class derived1 : public base 
        { 
           virtual void foo() {};
           virtual DerivedType GetType() { return One; }
        }; 
        
        template <typename T> 
        class derived2 : public base 
        { 
           virtual void foo() {};
            virtual DerivedType GetType() { return Two; }
        }; 
        

        解决方案2:使用标签类:

        class Base
        {
        public:
            virtual ~Base() { }
        };
        
        class Derived1Tag
        { };
        
        class Derived2Tag
        { };
        
        template <class T>
        class Derived1 : public Base, public Derived1Tag
        { };
        
        template <class T>
        class Derived2 : public Base, public Derived2Tag
        { };
        
        
        int main(int argc, char** argv)
        {
            Derived1<int> d1;
            Derived2<int> d2;
        
            cout << dynamic_cast<Derived1Tag*>((Base*)&d1) << endl;
            cout << dynamic_cast<Derived1Tag*>((Base*)&d2) << endl;
        
            return 0;
        }
        

        【讨论】:

        • 我必须说@EmileCormier 是对的,而且这可能反映了某个地方的错误设计选择。如果您要运行的函数取决于类型,那么它可能从一开始就应该是一个虚函数。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-01-13
        • 2020-03-16
        • 1970-01-01
        • 2013-09-23
        相关资源
        最近更新 更多