我正在寻找一种能够找到类似方法的技术。问题如下。我需要一个容器上的find方法,不需要修改容器内容来进行搜索。但是应该有一个const和一个非const版本,因为它可能导致在返回迭代器而不是const_iterator的情况下修改容器。 在这两种情况下,代码将完全相同,只有访问器将被评估为constXXX或XXX,编译器才能完成这项工作。从设计和维护的角度来看,将这两种方法实施两次看起来并不聪明。 (而且我真的想避免使用宏......) 我的意思也很好地说明了stl_tree.h中stl的gcc实现的那段代码:
template<typename _Key, typename _Val, typename _KeyOfValue,
typename _Compare, typename _Alloc>
typename _Rb_tree<_Key, _Val, _KeyOfValue,
_Compare, _Alloc>::iterator
_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
find(const _Key& __k)
{
iterator __j = _M_lower_bound(_M_begin(), _M_end(), __k);
return (__j == end()
|| _M_impl._M_key_compare(__k,
_S_key(__j._M_node))) ? end() : __j;
}
template<typename _Key, typename _Val, typename _KeyOfValue,
typename _Compare, typename _Alloc>
typename _Rb_tree<_Key, _Val, _KeyOfValue,
_Compare, _Alloc>::const_iterator
_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
find(const _Key& __k) const
{
const_iterator __j = _M_lower_bound(_M_begin(), _M_end(), __k);
return (__j == end()
|| _M_impl._M_key_compare(__k,
_S_key(__j._M_node))) ? end() : __j;
}
您可以看到方法的原型不同,但实现中编写的代码实际上是相同的。
我提出了两种可能的解决方案: 第一个是const_cast,另一个是辅助模板结构。 我在这里提出了这两种方法的一个简单例子:
#include <iostream>
using namespace std;
struct Data
{
typedef int* iterator;
typedef const int* const_iterator;
int m;
Data():m(-3){}
};
struct A : public Data
{
const_iterator find(/*const Key& k */) const
{
A *me = const_cast < A* > ( this );
return const_iterator( me->find(/*k*/) );
}
iterator find(/*const Key& k */){
return &m; }
};
//the second one is with the use of an internal template structure:
struct B : public Data
{
template<class Tobj, class Titerator>
struct Internal
{
Titerator find( Tobj& obj/*, const Key& k */ ){
return &(obj.m); }
};
const_iterator find( /*const Key& k */ ) const
{
Internal<const B, const_iterator> internal;
return internal.find( *this/*, k*/ );
}
iterator find( /*const Key& k */ )
{
Internal<B,iterator> internal;
return internal.find( *this/*, obs*/ );
}
};
int main()
{
{
A a;
a.find();
A::iterator it = a.find();
cout << *it << endl;
const A& a1(a);
A::const_iterator cit = a1.find();
cout << *cit << endl;
}
{
B b;
b.find();
B::iterator it = b.find();
cout << *it << endl;
const B& b1(b);
B::const_iterator cit = b1.find();
cout << *cit << endl;
}
}
这可能是一个众所周知的问题,我想知道一些c ++专家是否提出了一个好的设计模式来解决这个问题。特别是我想知道是否有人用这两种方法中的一种看到问题(特别是在表现方面)。因为第一个更容易理解我更喜欢它,特别是在阅读之后: Constants and compiler optimization in C++ 这似乎让我不要害怕写一个const_cast并打破我的表演。
提前谢谢,干杯,
曼努埃尔
答案 0 :(得分:1)
使用相同的实现在const和非const成员函数之间共享代码的惯用方法是非常量的const_cast
:
struct foo
{
const int* bar() const;
int* bar()
{
const int* p = static_cast<const foo*>(this)->bar();
// Perfectly defined since p is not really
// const in the first place
return const_cast<int*>(p);
}
};
这项工作提供了bar
的返回值是bar
的成员对象,当你调用非const bar
时,它实际上不是const(因此{{1}是合法的。)
你不能在const one中编写非const版本和const_cast
:这是未定义的行为。只有当对象首先不是const时,才允许删除constness 。
在您的示例代码中,由于您使用裸指针,您可以执行以下操作:
const_cast
但是只要你使用更复杂的迭代器类型,这就不行了:实际上,没有标准容器“struct A : public Data
{
const_iterator find(const Key& k) const
{
// The real implementation of find is here
}
iterator find(const Key& k)
{
// Not the other way around !
const_iterator p = static_cast<const A*>(this)->find(k);
return const_cast<iterator>(p);
}
};
到const_iterator
的转换,所以你被搞砸了,除非你使用普通指针。
一种解决方案是尽可能地排除因素,以便你可以iterator
,并在最后制造一个迭代器。
答案 1 :(得分:0)
可能没有很好的解决方案。 const重载和iterator/const_iterator
都是相当笨拙的工具。
在第一种情况下,让const版本完成工作并且非const版本执行转换可能更好。这样编译器就可以检查你的算法是否确实没有修改容器。
将const_iterator
转换为iterator
可能有点尴尬,因为它取决于实现细节。但你可以制作一个私人帮手将其封装在一个地方。
struct A : public Data
{
iterator find(/*const Key& k */)
{
const A *me = this;
return remove_const_from( me->find(/*k*/) );
}
const_iterator find(/*const Key& k */) const{
return &m; }
private:
//could be also static, but in the general case, *this might be needed
iterator remove_const_from(const_iterator p)
{
//in this case just a const_cast
return const_cast<int*>(p);
}
};
在第二种情况下,您可以通过使用模板函数及其至少推断出参数类型的能力来减少一些详细程度。
struct B : public Data
{
struct Internal //eventually, could be just a free function?
{
template<class Titerator, class Tobj>
static Titerator find( Tobj& obj/*, const Key& k */ ){
return &(obj.m); }
};
const_iterator find( /*const Key& k */ ) const
{
return Internal::find<const_iterator>( *this/*, k*/ );
}
iterator find( /*const Key& k */ )
{
return Internal::find<iterator>( *this/*, obs*/ );
}
};