2

我正在寻找一种技术来寻找类似的方法。问题如下。我需要一个容器上的 find 方法,该方法不需要修改容器内容来进行搜索。但是,它应该有一个 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 并破坏我的表现。

提前谢谢你,干杯,

曼努埃尔

4

2 回答 2

1

在具有相同实现的 const 和非 const 成员函数之间共享代码的惯用方法是const_cast在非 const 中:

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_cast是合法的)。

您不能在 const 版本中编写非常量版本const_cast:这是未定义的行为。仅当对象首先不是 const 时,才允许删除constness。

在您的示例代码中,由于您使用裸指针,您可以执行以下操作:

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_iteratoriterator所以你被搞砸了,除非你使用普通指针。

一种解决方案是尽可能地分解出尽可能多的因素const_cast,并在最后制造一个迭代器。

于 2011-09-11T10:03:02.837 回答
0

可能没有很好的解决方案。const 重载iterator/const_iterator都是相当笨拙的工具。

在第一种情况下,让 const 版本完成工作,让非 const 版本进行转换可能会更好。这样编译器就可以检查你的算法是否确实没有修改容器。

将 aconst_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*/ );
  }
};
于 2011-09-11T09:21:49.817 回答