当T2是c ++中T1的子类时,如何使用向量<t2>代替向量<t1>?</t1> </t2>

时间:2012-12-01 18:42:06

标签: c++ templates casting polymorphism

让我们对我的目标有所了解。我有一个抽象的Optimizer类,定义了各种算法的接口。它的工作原理是生成并消耗称为Request的未知数量的数据结构,因此我让它使用它们的向量。

class Request {
public:
    vector<double> x;
    double y;
    int id;
    // [...]
}

class Asynch_Optimizer {
public:
    virtual int generate_requests( vector<Request> & work ) = 0;
    virtual int assimilate_results( const vector<Request> & result ) = 0;
protected:
    virtual void convergence_test( const vector<Request> & population );
    // [...]
}

事实证明,Particle_Swarm_Optimizer可以使用Request中的一些其他字段。 (我在这里闻到了我的谬误,它只在内部使用其他字段。对于请求处理机制它没有任何意义。我将其分类为粒子只是为了保持一切很好地绑定在一起)

class Particle : public Request {
public:
    vector<double> v;
    vector<double> x_min;
    double min_value;
    // [...]
}

现在我想调用通用收敛测试方法,在Particle_Swarm_Optimizer的超类中实现,其中数据在Particle的向量中。有没有一种更有效的方法来转换为Request的向量,而不是构造一个新的向量并单独复制每个元素:

void opti::Particle_Swarm_Optimizer::convergence_test( const vector<Particle> & population )
{
    //TODO remove slow hack 
    vector<Request> cast_up;
    for (size_t i = 0; i < population.size(); i++) {
        cast_up.push_back( population[i] );
    }
    Asynch_Optimizer::convergence_test( cast_up );
}

我假设有更好的方法来构建此示例的数据。如果有一种方法来升级容器的模板类型,我仍然很好奇吗?

3 个答案:

答案 0 :(得分:2)

即使您在容器中使用指针,也无法完成。并且有充分的理由不这样做。

即使SubClassBaseClass的子类型,vector<SubClass*>也不是vector<BaseClass*>的子类型。这就是为什么这不可能是真的(对于任何类型的容器,而不仅仅是向量):

假设vector<SubClass*> 视为vector<BaseClass*>的子类,并假设还有第二个类OtherSubClass也来自BaseClass 。考虑一下这段代码

// This looks logical and is type-safe.
void InsertElement(vector<BaseClass*>& container, BaseClass* element) {
   container.push_back(element);
}

int main() {
  vector<SubClass*> subclass_container;

  // Ok, fine, inserting a SubClass pointer into a vector of
  // SubClass pointers.
  SubClass subclass_obj;
  InsertElement(subclass_container, &subclass_obj);

  // But what about this? Now we're able to insert an 
  // OtherSubClass pointer into that same container!
  OtherSubClass othersubclass_obj;
  InsertElement(subclass_container, &othersubclass_obj);

  // Suddenly, we have a SubClass* that actually points at an
  // OtherSubClass object, when these two are siblings!
  SubClass* subclass_ptr = subclass_container[1];
}

所以,如果你采用vector<SubClass*>应该被视为vector<BaseClass*>的子类的想法,那么你最终会得到两个代码,这些代码显然是类型安全的,但最终会让你以编译器无法察觉的方式违反类型安全。

答案 1 :(得分:0)

一种解决方案是将优化器定义为类模板,并使用std::enable_if使其适用于类型Request或其派生类(未经测试):

template
<
  typename R, //Request or its derived classes
  typename A = typename std::enable_if
                        <
                           std::is_same<Request, R>::value ||
                           std::is_base_of<Request, R>::value 
                        >::type
>
class Asynch_Optimizer {
public:
    virtual int generate_requests( vector<R> & work ) = 0;
    virtual int assimilate_results( const vector<R> & result ) = 0;
protected:
    virtual void convergence_test( const vector<R> & population );
}

答案 2 :(得分:0)

如果你想保持它几乎就像现在一样,即如果你想要运行时多态,那么我会想到any_iterator的概念。

您可以定义一个特殊的多态迭代器,它从底层容器和底层类型中抽象出来。这里有一个小小的片段来获得这个想法(它错过了一些方法来考虑一个合适的迭代器,但不应该很难自己实现其余的)

template<class T>
class any_input_iterator : public std::iterator< std::input_iterator_tag,T> {
public:
    any_input_iterator() {}
    any_input_iterator(const any_input_iterator & other) : piter(other.piter->clone()) {}

    template<class Iter>
    any_input_iterator(Iter iter) : piter( new iterator_impl<Iter>(iter)) {}

    T& operator*() {return piter->reference();}

    bool operator==(const any_input_iterator & other) const {
        if ( typeid(*piter) != typeid(* other.piter) ) return false;
        return piter->equal(*other.piter);
    }
    any_input_iterator& operator++() {piter->advance(); return *this;}

private:
    struct iterator_base {
       iterator_base() {}
       virtual ~iterator_base() {}
       virtual void advance() = 0;
       virtual T& reference() = 0;
       virtual iterator_base* clone() = 0;
       virtual bool equal(const iterator_base & other) const = 0;
    };

   template<class InputIterator> struct iterator_impl  : public iterator_base{
       iterator_impl(InputIterator t) : m_iter(t) {}
       virtual void advance() { ++m_iter;}
       virtual T& reference() { return *m_iter;} //assuming this is implicitly castable to T&
       virtual iterator_base* clone() {return new iterator_impl(m_iter);}
       virtual bool equal(const iterator_base & other) const {
        return m_iter ==  dynamic_cast<const iterator_impl&>(other).m_iter;
       }

       InputIterator m_iter;
   };

   std::unique_ptr<iterator_base> piter;
};

由于您的算法在编译时无法知道它们运行的​​确切类型,因此标准迭代器几乎没用。根据这种代理迭代器编写算法可以解决问题。

此外,在我看来,根据容器而不是迭代器对编写算法是个坏主意。我会根据迭代器对重构代码,但是如果你仍然想要使用向量,你可以使用any_iterator来编写any_vector_reference类。

所有这些事情只有在你有相同类型的序列时才有效(如果你没有,你必须使用指针向量)。