我正在尝试创建自己的boost::adaptors::transformed
。
以下是相关的boost code。
以下是它的用法(从a SO answer by LogicStuff修改): -
C funcPointer(B& b){
//"funcPointer" is function convert from "B" to "C"
return instance-of-C
}
MyArray<B> test; //<-- any type, must already have begin() & end()
for(C c : test | boost::adaptor::transformed(funcPointer)) {
//... something ....
}
结果将与: -
相同for(auto b : test) {
C c = funcPointer(b);
//... something ...
}
我创建的CollectAdapter
旨在像boost::adaptor::transformed
一样工作
它在大多数常见情况下都能正常工作
有问题的部分是CollectAdapter
- 我图书馆的核心
我不知道是否应该缓存collection_
按指针或按值。
CollectAdapter 封装基础collection_
(例如指向std::vector<>
的指针): -
template<class COLLECTION,class ADAPTER>class CollectAdapter{
using CollectAdapterT=CollectAdapter<COLLECTION,ADAPTER>;
COLLECTION* collection_; //<---- #1 problem? should cache by value?
ADAPTER adapter_; //<---- = func1 (or func2)
public: CollectAdapter(COLLECTION& collection,ADAPTER adapter){
collection_=&collection;
adapter_=adapter;
}
public: auto begin(){
return IteratorAdapter<
decltype(std::declval<COLLECTION>().begin()),
decltype(adapter_)>
(collection_->begin(),adapter_);
}
public: auto end(){ ..... }
};
IteratorAdapter
(上面用过)封装了底层迭代器,改变了operator*
的行为: -
template<class ITERATORT,class ADAPTER>class IteratorAdapter : public ITERATORT {
ADAPTER adapter_;
public: IteratorAdapter(ITERATORT underlying,ADAPTER adapter) :
ITERATORT(underlying),
adapter_(adapter)
{ }
public: auto operator*(){
return adapter_(ITERATORT::operator*());
}
};
CollectAdapterWidget
(在下面使用)只是构建 CollectAdapter
-instance的辅助类。
可以像: -
一样使用int func1(int i){ return i+10; }
int main(){
std::vector<int> test; test.push_back(5);
for(auto b:CollectAdapterWidget::createAdapter(test,func1)){
//^ create "CollectAdapter<std::vector<int>,func1>" instance
//here, b=5+10=15
}
}
上述代码在大多数情况下都可以正常工作,除非COLLECTION
是临时对象。
更具体地说,当我创建适配器适配器 适配器 时,可能会发生悬空指针。
int func1(int i){ return i+10; }
int func2(int i){ return i+100; }
template<class T> auto utilityAdapter(const T& t){
auto adapter1=CollectAdapterWidget::createAdapter(t,func1);
auto adapter12=CollectAdapterWidget::createAdapter(adapter1,func2);
//"adapter12.collection_" point to "adapter1"
return adapter12;
//end of scope, "adapter1" is deleted
//"adapter12.collection_" will be dangling pointer
}
int main(){
std::vector<int> test;
test.push_back(5);
for(auto b:utilityAdapter(test)){
std::cout<< b<<std::endl; //should 5+10+100 = 115
}
}
这将导致运行时错误。这是the dangling-pointer demo。
在实际使用中,如果界面更加精彩,例如使用|
运算符,将更难以检测到错误: -
//inside "utilityAdapter(t)"
return t|func1; //OK!
return t|func1|func2; //dangling pointer
如何改善我的库以修复此错误,同时保持性能&amp; 健壮性&amp; 可维持接近同一水平?
换句话说,如何优雅地缓存COLLECTION
(可以是适配器或真实数据结构)的数据或指针?
或者,如果从头开始编码(而不是修改我的代码)更容易回答,那就去吧。 :)
当前代码通过指针缓存 解决方法的主要思想是缓存按值。
让适配器缓存COLLECTION
的值。
这是主要的变化: -
COLLECTION collection_; //<------ #1
//changed from .... COLLECTION* collection_;
缺点: -
std::vector
)将被价值复制 - 浪费资源
(直接用于std::vector
时)我将创建2个版本的库 - AdapterValue
和AdapterPointer
我还必须创建相关的类(Widget
,AdapterIterator
等)。
AdapterValue
- 按值。 (专为utilityAdapter()
设计)AdapterPointer
- 指针。 (专为std::vector
设计)缺点: -
我可以使用模板专业化来执行此操作: -
If( COLLECTION is an "CollectAdapter" ){ by value }
Else{ by pointer }
缺点: -
很抱歉很长的帖子。
答案 0 :(得分:0)
我个人将模板专门化–但是,不是专门针对原始模板,而是嵌套类:
template<typename Collection, typename Adapter>
class CollectAdapter
{
template<typename C>
class ObjectKeeper // find some better name yourself...
{
C* object;
public:
C* operator*() { return object; };
C* operator->() { return object; };
};
template<typename C, typename A>
class ObjectKeeper <CollectAdapter<C, A>>
{
CollectAdapter<C, A> object;
public:
CollectAdapter<C, A>* operator*() { return &object; };
CollectAdapter<C, A>* operator->() { return &object; };
};
ObjectKeeper<Collection> keeper;
// now use *keeper or keeper-> wherever needed
};
然后,外部类通过始终使用指针来覆盖这两种情况,而嵌套类将差异隐藏起来。
当然,不完整(例如,您还需要向外部和内部类中添加适当的构造函数),但这应该可以给您一个主意...
您甚至可以允许用户选择是否要复制:
template<typename Collection, typename Adapter, bool IsAlwaysCopy = false>
class CollectAdapter
{
template<typename C, bool IsCopy>
class ObjectWrapper // find some better name yourself...
{
C* object;
public:
C* operator*() { return object; };
C* operator->() { return object; };
};
template<typename C>
class ObjectWrapper<C, true>
{
C object;
public:
C* operator*() { return &object; };
C* operator->() { return &object; };
};
// avoiding code duplication...
template<typename C, bool IsCopy>
class ObjectKeeper : public ObjectWrapper<C, IsCopy>
{ };
template<typename C, typename A, bool IsCopy>
class ObjectKeeper <CollectAdapter<C, A>, IsCopy>
: public ObjectWrapper<CollectAdapter<C, A>, true>
{ };
ObjectKeeper<Collection> keeper;
};
答案 1 :(得分:0)
在我的indexed_view
中,如果集合是右值,则存储集合的值;如果是左值,则存储引用。您可以在此处执行相同操作:重载operator|
的右值和左值。
template<typename Collection,typename Filter>
auto operator|(Collection&& collection,Filter filter){
return create_adapter_for_rvalue_collection(collection,filter);
}
template<typename Collection,typename Filter>
auto operator|(Collection const& collection,Filter filter){
return create_adapter_for_const_lvalue_collection(collection,filter);
}
template<typename Collection,typename Filter>
auto operator|(Collection & collection,Filter filter){
return create_adapter_for_non_const_lvalue_collection(collection,filter);
}