Я ищу технику, чтобы найти аналогичные методы.Проблема в следующем.Мне нужен метод find для контейнера, который не должен изменять содержимое контейнера для поиска.Однако должна существовать как const, так и неконстантная версия, поскольку это может привести к модификации контейнера в случае, если вместо const_iterator возвращается итератор.В этих двух случаях код будет точно таким же, только методы доступа будут оценены как constXXX или XXX, и компилятор выполнит эту работу.Как бы то ни было, с точки зрения дизайна и поддержки, не представляется разумным, чтобы эти два метода были реализованы два раза.(И я действительно хотел бы избежать использования макроса для этого ...) То, что я имею в виду, также очень хорошо иллюстрируется этим фрагментом кода из gcc-реализации stl в stl_tree.h:
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 ++ , что, кажется, позволяет мне не бояться писать const_cast и ломать мои выступления.
Заранее спасибо, ура,
Мануэль