C++ 创建用于重写迭代器的运算符*()的库-风险悬空指针
我正在努力创造我自己的 这里是相关的 以下是它的用法(修改自):- 我的尝试 我创建了C++ 创建用于重写迭代器的运算符*()的库-风险悬空指针,c++,c++11,boost,iterator,adapter,C++,C++11,Boost,Iterator,Adapter,我正在努力创造我自己的 这里是相关的 以下是它的用法(修改自):- 我的尝试 我创建了CollectAdapter,旨在像boost::adapter::transformed 在大多数情况下,它都可以正常工作 这是完整的和完整的。(与以下代码相同) 有问题的部分是CollectAdapter——我的库的核心。 我不知道是应该按指针缓存集合还是按值缓存 CollectAdapter封装底层集合(例如指向std::vector)的指针:- CollectAdapterWidget(下面使用)只是一
CollectAdapter
,旨在像boost::adapter::transformed
在大多数情况下,它都可以正常工作 这是完整的和完整的。(与以下代码相同) 有问题的部分是
CollectAdapter
——我的库的核心。我不知道是应该按指针缓存
集合
还是按值缓存
CollectAdapter封装底层集合
(例如指向std::vector
)的指针:-
CollectAdapterWidget
(下面使用)只是一个帮助器类,用于构造CollectAdapter
-实例
它可以像这样使用:-
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
}
}
这将导致运行时错误。这是
在实际使用中,如果界面更棒,例如使用|
操作符,那么bug将更难被检测到:-
//inside "utilityAdapter(t)"
return t|func1; //OK!
return t|func1|func2; //dangling pointer
问题:
如何改进我的库以修复此错误,同时保持性能&健壮性&可维护性接近相同水平
换句话说,如何优雅地缓存集合
(可以是适配器或真实数据结构)的数据或指针
或者,如果从头开始编写代码(比修改代码)更容易回答问题,那就去做吧。:)
我的变通办法
当前代码通过指针缓存解决方法的主要思想是按值缓存 解决方案1(始终“按值”) 让适配器缓存
集合的值
主要变化如下:-
COLLECTION collection_; //<------ #1
//changed from .... COLLECTION* collection_;
缺点:-
- 许多适配器类之间没有很好的协作。
它们必须相互识别:已识别=应按值缓存
很抱歉发了这么长的邮件 我个人会选择模板专业化–但是,不是专门化原始模板,而是一个嵌套类:
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
};
模板
类集合适配器
{
模板
类ObjectKeeper//自己找一个更好的名字。。。
{
C*对象;
公众:
C*运算符*(){return object;};
C*运算符->(){return object;};
};
模板
类对象管理员
{
采集适配器对象;
公众:
CollectAdapter*运算符*(){return&object;};
CollectAdapter*运算符->(){return&object;};
};
对象管理员;
//现在在需要的地方使用*keeper或keeper->
};
然后,外部类通过始终使用指针来覆盖这两种情况,而嵌套类则隐藏差异
当然,是不完整的(例如,您还需要向外部类和内部类添加适当的构造函数),但它应该让您了解
您甚至可以允许用户选择是否要复制:
模板
类集合适配器
{
模板
类ObjectWrapper//自己找到更好的名称。。。
{
C*对象;
公众:
C*运算符*(){return object;};
C*运算符->(){return object;};
};
模板
类对象包装器
{
C对象;
公众:
C*运算符*(){return&object;};
C*运算符->(){return&object;};
};
//避免代码重复。。。
模板
类ObjectKeeper:PublicObjectWrapper
{ };
模板
类对象管理员
:公共对象包装器
{ };
对象管理员;
};
在my中,如果集合是右值,则存储集合的值;如果集合是左值,则存储引用。在这里也可以这样做:为右值和左值重载操作符|
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);
}
模板
自动运算符|(收集和收集,过滤器){
返回为右值集合(集合、筛选器)创建适配器;
}
模板
自动运算符|(采集常量和采集、过滤器){
返回create_adapter_for_const_lvalue_collection(collection,filter);
}
模板
自动操作员|(收集和收集,过滤器){
返回为非常量左值集合(集合、筛选器)创建适配器;
}
在存储(因此使用)适配器时,如果不查看您的代码,所有增压范围适配器都有一个共同的陷阱,即悬挂引用。我经常烧伤自己,所以我采取了避免范围适配器的措施,除非非适配器代码绝对无效unwieldy@sehe这是非常有用的。谢谢。这里有一个参考案例,很高兴知道我的目标是做一些比boost更好的事情,尽管这可能不切实际或非常困难。:)我建议看看Niebler的Range-V3方案(公开实施)看起来您的ObjectWrapper
与std::reference\u wrapper
非常相似:我可以通过专门研究std::reference\u wrapper
看到一个版本,所以OP的代码应该是autoadapter1=collectoradapterwidget::createAdapter(std::ref(t),func1)代码>自动适配器12=CollectAdapterWidget::createAdapter(适配器1,函数2)代码>。这需要通过引用(典型情况)使用std::ref
。也许它可以翻转,所以ref是默认值,但value是可选的?按照这些思路,可能(但更危险的是)有一个版本使用存储引用的const T&
的第一个参数,另一个版本使用存储引用的T&
。然后createAdapter(createAdapter(t,func1),func2)
将拥有内部适配器,但内部适配器将只具有对t的引用。我喜欢&
部分
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
}
}
//inside "utilityAdapter(t)"
return t|func1; //OK!
return t|func1|func2; //dangling pointer
COLLECTION collection_; //<------ #1
//changed from .... COLLECTION* collection_;
If( COLLECTION is an "CollectAdapter" ){ by value }
Else{ by pointer }
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;
};
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);
}