2

让我们更具体地说明我在做什么。我有一个抽象的优化器类,它为各种算法定义了一个接口。它通过产生和消耗未知数量的这种称为请求的数据结构来工作,因此我让它使用它们的向量。

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 可以在请求中使用一些额外的字段。(我在这里闻到了我的谬误,它仅在内部使用附加字段。对于请求处理机制,它没有任何意义。我将其分类为粒子只是为了将所有内容很好地绑定在一起)

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

现在我想调用通用收敛测试方法,在 Particle_Swarm_Optimizer 的超类中实现,其中数据在粒子向量中。有没有比构造新向量和单独复制每个元素更有效的方法来转换为请求向量:

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 );
}

我假设有更好的方法来构造这个例子的数据。我仍然很好奇是否存在向上转换容器模板类型的方法?

4

3 回答 3

2

即使您在容器中使用指针,也无法完成。有一个很好的理由为什么不这样做。

即使SubClass是 的子类型BaseClass,也不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*>两个代码,这些代码显然是类型安全的,但最终允许您以一种无法检测到的方式违反类型安全编译器。

于 2012-12-01T18:51:45.437 回答
0

If you want to keep it almost like it is now, i.e. if you want runtime polymorphism, the concept of any_iterator comes to mind.

You can define a special polymorphic iterator which abstracts from both the underlying container and the underlying type. Here is a little snippet to get the idea (it misses some methods to be consider a proper iterator, but shouldn't be difficult to implement the rest by yourself)

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;
};

Since your algorithms can not know at compile time which is the exact type on which they operate, standard iterators are of little use. Writing your algorithm in terms of this kind-of proxy iterator solves the problem.

Moreover, in my opinion is just a bad idea to write algorithms in terms of container instead of iterator pairs. I'd refactor the code in terms of iterator pairs, but if you still want to stick with vectors, you can make use of any_iterator to write an any_vector_reference class.

All these things will work only if you have sequences of the same type (if you don't, you have to work with vector of pointers).

于 2012-12-01T19:33:11.617 回答
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 );
}
于 2012-12-01T18:57:16.307 回答