让我们对我的目标有所了解。我有一个抽象的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 );
}
我假设有更好的方法来构建此示例的数据。如果有一种方法来升级容器的模板类型,我仍然很好奇吗?
答案 0 :(得分: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*>
的子类的想法,那么你最终会得到两个代码,这些代码显然是类型安全的,但最终会让你以编译器无法察觉的方式违反类型安全。
答案 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
类。
所有这些事情只有在你有相同类型的序列时才有效(如果你没有,你必须使用指针向量)。