管理模板化派生类型的向量集合
Managing a collection of vectors of templated derived types
X:我想做什么:
我有类型:BaseType
和DerivedType<int k>
(请参阅下面的代码),并且我需要处理派生类型std::vector<DerivedType<k>>
和k = 1...K
的K
向量的集合。我想访问这些向量中的对象,并对它们执行依赖于k
的操作。K
是一个编译时间常数。问题在实现中得到了说明:
类型定义为:
#include <iostream>
#include <algorithm>
struct BaseType { // Interface of the DerivedTypes
virtual void print(){std::cout << "BaseType!" << std::endl; }
};
template< int k >
struct DerivedType : public BaseType {
static const int k_ = k;
// ... function calls templated on k ...
void print(){std::cout << "DerivedType: " << k_ << std::endl;}
};
template< int k >
void doSomething ( DerivedType<k>& object ) { object.print(); }
我想做的是:
int main() {
// My collection of vectors of the derived types:
std::vector<DerivedType<0>> derType0(2);
std::vector<DerivedType<1>> derType1(1);
std::vector<DerivedType<2>> derType2(3);
// ... should go to K: std::vector<DerivedType<K>> derTypeK;
// Iterate over the derived objects applying a k-dependent templated function:
std::for_each(begin(derType0),end(derType0),[](DerivedType<0>& object){
doSomething<0>(object);
});
std::for_each(begin(derType1),end(derType1),[](DerivedType<1>& object){
doSomething<1>(object);
});
std::for_each(begin(derType2),end(derType2),[](DerivedType<2>& object){
doSomething<2>(object);
});
return 0;
}
我想避免重复代码,这样我只需要更改K
,它是O(10)
的编译时常数。理想情况下,我会有一个"更像"这样的东西:
// Pseudocode: do not try to compile this
create_derived_objects(DerivedType,K)
= std::vector< std::vector<DerivedType<k>>* > my_K_derived_types;
for each vector<DerivedType<k>>* derivedTypes in my my_K_derived_types
for each object in (*derivedTypes)
doSomething<k> on object of type derivedType<k>
// I could also restrict doSomething<k> to the base interface
派生类型的每个向量都包含O(10^6)
到O(10^9)
对象。最内部的循环是我的应用程序中最耗时的部分,因此dynamic_cast只是最外部循环的一个选项。
Y:我尝试过但没有成功
我现在正在学习Abrahams C++模板元编程的书,看看我是否可以使用boost::mpl
。我也在做boost::fusion
的教程,看看我是否也可以使用它。然而,这些库的学习曲线相当大,所以我想在投资一周之前先问一下,什么时候可以找到更好、更简单的解决方案。
我的第一次尝试是包装我的向量std::vector<DerivedType<k>>
,这样我就可以创建一个vector<WrappedDerivedTypes*>
,并在for_each
循环中分别访问每个单个向量。然而,在循环中,我有一系列if(dynamic_cast<std::vector<DerivedType<0>>>(WrappedVector) != 0 ){ do for_each loop for the derived objects } else if( dynamic_cast...) { do...} ...
,我无法消除。
基于向量的通用链表、策略模式和通过链表递归应用策略的递归解决方案怎么样?(注意:见最后的改进版本):
#include <iostream>
#include <vector>
template <int j>
class holder {
public:
const static int k = j;
};
template <int j>
class strategy {
public:
void operator()(holder<j> t)
{
std::cout << "Strategy " << t.k << std::endl;
}
};
template <int k>
class lin_vector {
private:
std::vector<holder<k>> vec;
lin_vector<k-1> pred;
public:
lin_vector(const lin_vector<k-1> &pred, std::vector<holder<k>> vec)
: vec(vec), pred(pred) { }
std::vector<holder<k>> get_vec() { return vec; }
lin_vector<k-1> &get_pred() { return pred; }
};
template <>
class lin_vector<0> {
public:
lin_vector() { }
};
template <int k, template <int> class strategy>
class apply_strategy {
public:
void operator()(lin_vector<k> lin);
};
template <int k, template <int> class strategy>
void apply_strategy<k, strategy>::operator()(lin_vector<k> lin)
{
apply_strategy<k-1, strategy>()(lin.get_pred());
for (auto i : lin.get_vec())
strategy<k>()(i);
}
template <template <int> class strategy>
class apply_strategy<0, strategy>
{
public:
void operator()(lin_vector<0> lin) { /* does nothing */ }
};
template <int k>
lin_vector<k> build_lin()
{
return lin_vector<k>(build_lin<k-1>(), {holder<k>()});
}
template <>
lin_vector<0> build_lin()
{
return lin_vector<0>();
}
int main(void)
{
apply_strategy<5, strategy>()(build_lin<5>());
}
用C++11编译器编译它。您可能会发现构建lin_vector
需要大量复制这一事实并不令人满意,但您可以专门化结构以满足您的需求(也许用指针替换pred
,或者将创建策略直接嵌入到链表中)。
EDIT:这里有一个改进的版本,它避免了大量的复制,并以更连贯和统一的方式处理列表构建和处理:
#include <iostream>
#include <vector>
template <int j>
class holder {
public:
const static int k = j;
};
template <int k>
class lin_vector {
private:
std::vector<holder<k>> vec;
lin_vector<k-1> pred;
public:
std::vector<holder<k>> &get_vec() { return vec; }
lin_vector<k-1> &get_pred() { return pred; }
};
template <>
class lin_vector<0> {
public:
lin_vector() { }
};
template <int k, template <int> class strategy>
class apply_strategy {
public:
void operator()(lin_vector<k> &lin);
};
template <int k, template <int> class strategy>
void apply_strategy<k, strategy>::operator()(lin_vector<k> &lin)
{
apply_strategy<k-1, strategy>()(lin.get_pred());
strategy<k>()(lin.get_vec());
}
template <template <int> class strategy>
class apply_strategy<0, strategy>
{
public:
void operator()(lin_vector<0> &lin) { /* does nothing */ }
};
template <int j>
class strategy {
public:
void operator()(std::vector<holder<j>> &t)
{
std::cout << "Strategy " << j << ", elements: ";
for (auto v : t)
std::cout << v.k << " ";
std::cout << std::endl;
}
};
template <int j>
class build_strategy {
public:
void operator()(std::vector<holder<j>> &t)
{
for (unsigned int i = 0; i < j; i++)
t.push_back(holder<j>());
}
};
int main(void)
{
const int K = 5;
lin_vector<K> list;
apply_strategy<K, build_strategy>()(list);
apply_strategy<K, strategy>()(list);
}
一个没有虚拟调度的解决方案是可能的,尽管这可能有些过头了。
你需要的第一件事是一个函数模板doSomething<K>()
,你专门研究每种派生类型:
template <int K>
void doSomething(vector<DerivedType<K> >& x);
template <>
void doSomething<1>(vector<DerivedType<1> >& x) { ... }
template <>
void doSomething<2>(vector<DerivedType<2> >& x) { ... } // etc.
然后,您可以使用递归定义的struct
模板构建一个强类型的向量集合:
template <int K>
struct vov {
vov<K - 1> prev;
vector<DerivedType<K> > v;
};
template <>
struct vov<1> {
vector<DerivedType<1> > v;
};
最后,您可以编写一个递归函数模板来处理这种结构:
template <int K>
void process(vov<K>& x) {
doSomething(x.v); // Type inference will find the right doSomething()
process(x.prev); // Here too
}
template <>
void process<1>(vov<1>& x) {
doSomething(x.v);
}
现在主要代码看起来像:
vov<42> foo;
process(foo);
由于process()
函数调用通过递归进行迭代,因此可能会不必要地使用K个堆栈帧;然而,它是尾部递归,现代优化C++编译器通常可以将其转换为无堆栈浪费的简单迭代。使用尾部递归迫使我们以"相反"的顺序处理向量,以便最后处理DerivedType<1>
向量,但如有必要,可以使用两个int
模板参数(一个将向另一个"向上计数",而不是一个向1"向下计数"的int
参数)使用稍微复杂的模板来解决这一问题。
注意,在这个解决方案中,从BaseType
派生每个DerivedType<k>
不会带来任何好处——除非出于不同的原因需要BaseType
,否则您可能会完全忘记它
很可能有一些MPL原语可以简化其中的一些过程——如果有人知道它们,请随意编辑。
- 如何将ampl中的集合表示为c++中的向量
- 如何写向量的无序向量集,即unordered_set<向量<向量<int>>集合?
- 从具有自定义排序的向量创建集合
- 在 c++ 中将集合转换为向量和向量转换为字符串时出现运行时异常
- 将字符串集合中的元素添加到字符串集合的向量中
- 如何在一对集合的向量中插入元素?vector<pair<int,set<string>>>
- 我可以在一行代码中将向量中与条件匹配的所有元素插入到集合中吗?
- 创建集合向量作为类成员会在 c++ 中给出错误
- 如何更有效地从向量或集合中擦除元素?
- 创建一个抽象类类型的集合,shared_ptr的抽象类向量
- 如何在 c++ 中将集合和数组合并到向量中
- 将索引存储在向量<集合<int>>与向量<向量中<int>>
- variadic宏可从未知参数集合中生成字符串向量
- 集合和向量.在C 中快速设置
- 将向量的元素添加到无序集合中
- 将集合转换为向量的向量
- 管理模板化派生类型的向量集合
- 在集合中查找结构向量
- 如何移除向量集合中向量的元素
- Visual Studio - 如何在 MS 编译器中使用数组初始化C++向量/集合