【问题标题】:How to provide stl like container with public const iterator and private non-const iterator?如何为类似 stl 的容器提供公共 const 迭代器和私有非 const 迭代器?
【发布时间】:2010-03-13 18:26:26
【问题描述】:

我有一个包含 std::list 的类,并希望为 const_iterator 提供 public begin() 和 end(),为普通迭代器提供 private begin() 和 end()。

但是,编译器看到私有版本并抱怨它是私有的,而不是使用公共 const 版本。

我知道 C++ 不会重载返回类型(在本例中为 const_iterator 和迭代器),因此它选择非 const 版本,因为我的对象不是 const。

在调用 begin() 之前将我的对象转换为 const 或不重载名称 begin 有没有办法做到这一点?

我认为这是人们以前解决过的已知模式,并且想效仿如何解决这个问题。

class myObject {
public:
  void doSomethingConst() const;
};

class myContainer {
public:
  typedef std::list<myObject>::const_iterator const_iterator;
private:
  typedef std::list<myObject>::iterator iterator;

public:
  const_iterator begin() const { return _data.begin(); }
  const_iterator end()   const { return _data.end();   }
  void reorder();
private:
  iterator begin() { return _data.begin(); }
  iterator end()   { return _data.end();   }
private:
  std::list<myObject> _data;
};

void myFunction(myContainer &container) {
  myContainer::const_iterator itr = container.begin();
  myContainer::const_iterator endItr = container.end();
  for (; itr != endItr; ++itr) {
    const myObject &item = *itr;
    item.doSomethingConst();
  }
  container.reorder(); // Do something non-const on container itself.
}

编译器的错误是这样的:

../../src/example.h:447: error: `std::_List_iterator<myObject> myContainer::begin()' is private
caller.cpp:2393: error: within this context
../../src/example.h:450: error: `std::_List_iterator<myObject> myContainer::end()' is private
caller.cpp:2394: error: within this context

谢谢。

-威廉

【问题讨论】:

  • 据我所知,您的代码中没有继承,可能您应该更正“我正在私下派生一个类”。
  • 糟糕,编辑不同步,感谢您指出,现在已更正。
  • 如果你的非常量 begin & end 对你的容器来说是私有的,你为什么要为它们提供私有函数呢?为什么不直接使用列表迭代器。
  • Arak,一个原因是如果 begin()/end() 被保护,而成员变量保持私有。

标签: c++ stl iterator constants overloading


【解决方案1】:

从 std::list 派生的坏主意(它并非旨在派生自)。

使用 std::list 类型的成员变量。

class myContainer
{
  std::list<myObject>   m_data;
  public:

    typedef std::list<myObject>::const_iterator myContainer::const_iterator;
  private:
    typedef std::list<myObject>::iterator myContainer::iterator;

  public:

    myContainer::const_iterator begin() const
    {
      return m_data.begin();
    }

    myContainer::const_iterator end() const 
    {
      return m_data.end();
    }

  private:
    myContainer::iterator begin()
    {
      return m_data.begin();
    }

    myContainer::iterator end()
    {
      return m_data.end();
    }
};

【讨论】:

  • 已更新问题以反映这个好建议,但主要问题仍然存在。似乎唯一的解决方案是在调用 begin()/end() 之前将私有的或 static_cast 重命名为 const。
【解决方案2】:

我认为您唯一的选择是重命名私有方法(如果您首先需要它们)。

另外我认为你应该重命名 typedefs:

class MyContainer
{
public:
     typedef std::list<Object>::const_iterator iterator;
     typedef iterator const_iterator;

     const_iterator begin() const;
     const_iterator end() const;

private:
     typedef std::list<Object>::iterator _iterator;
     _iterator _begin();
     _iterator _end();
     ...
};

容器应该同时定义iterator 和const_iterator。接受容器的非常量实例的通用函数可能期望使用iterator typedef - 即使它不会修改元素。 (例如BOOST_FOREACH。)

就 const 正确性而言,这很好,因为如果泛型函数实际上尝试修改对象,真正的迭代器类型(作为 const_iterator)不会允许这样做。

作为测试,以下应该与您的容器一起编译:

int main()
{
    myContainer m;
    BOOST_FOREACH(const myObject& o, m) 
    {}
}

请注意,m 不是 const,但我们只是试图获取对包含类型的 const 引用,因此应该允许这样做。

【讨论】:

  • Arg,比我早几秒发布!
  • @Emile: AFAIK,使用单个下划线后跟一个小写字符是可以的。就个人而言,我确实不会使用这种风格。
【解决方案3】:

您需要更改私有开始端的名称。编译器无法仅通过返回类型来区分

这对我有用:注意 _begin _end 名称

#include <list>


class myObject {};

class myContainer : private std::list<myObject> {
public:
    typedef std::list<myObject>::const_iterator const_iterator;
private:
    typedef std::list<myObject>::iterator iterator;

public:
  myContainer::const_iterator begin() const {
    return std::list<myObject>::begin();
  }
  myContainer::const_iterator end() const {
    return std::list<myObject>::end();
  }
private:
  myContainer::iterator _begin() {
    return std::list<myObject>::begin();
  }
  myContainer::iterator _end() {
    return std::list<myObject>::end();
  }
};

void myFunction(myContainer &container) {
  myContainer::const_iterator aItr = container.begin();
  myContainer::const_iterator aEndItr = container.end();
  for (; aItr != aEndItr; ++aItr) {
    const myObject &item = *aItr;
    // Do something const on container's contents.
  }
}

int main(){
    myContainer m;
    myFunction(m);
}

【讨论】:

  • 虽然重命名在这里可以解决问题,但如果您想将对象放入期望begin() 和end() 的泛型函​​数中,则会遇到麻烦...
  • 似乎这是除了为每个调用者执行 static_cast(container).begin()/end() 之外唯一可行的选择。
  • @Emilie Cormier 我不知道。谢谢!。在 python 中,用 _ 命名私有成员是一个常见的习惯用法。 @xtofl,如果这些函数是私有的,除了类本身之外没有人可以使用它,你在说什么通用函数?一个例子?
【解决方案4】:

您可能希望将方法 Myfunction 的签名更改为:

void myFunction(const myContainer &container) 

因为 const 方法只会在 const 对象上调用。当前发生的情况是您正在尝试调用一个非常量方法,在您的情况下它是私有的。

【讨论】:

  • 但在某些情况下这还不够,我们更新了测试用例来反映这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-01-28
  • 1970-01-01
  • 2015-09-04
  • 1970-01-01
  • 1970-01-01
  • 2021-09-05
  • 1970-01-01
相关资源
最近更新 更多