公开begin()和end()的const和nonconst版本,用智能指针迭代成员向量

时间:2014-08-06 06:07:55

标签: c++ const smart-pointers const-iterator

// Cat.h

class Cat
{public:
    void const_meow() const{ ... };
    void meow(){ ... };
};

class CatLibrary
{public:
    std::vector<std::shared_ptr<Cat>>::iterator begin()
    { 
        return m_cat_list.begin();
    }

    // compile error, the compiler complains cannot covert type
    //     from `std::vector<std::shared_ptr<Cat>>::const_iterator` 
    //     to   `std::vector<std::shared_ptr<const Cat>>::const_iterator`
    std::vector<std::shared_ptr<const Cat>>::const_iterator begin() const 
    { 
        return m_cat_list.cbegin();
    }
private:
    std::vector<std::shared_ptr<Cat>> m_cat_list;
};

// main.cpp

CatLibrary cat_library;       
cat_library.add(std::make_shared<Cat>());
cat_library.add(std::make_shared<Cat>());

for(auto& cat: cat_library )
{
   cat->const_meow(); 
   cat->meow(); 
}       
for(const auto& cat: cat_library)
{
   cat->const_meow(); 
   cat->meow();       // hope to compile error due to invoking non_const method of Cat.
}


const CatLibrary& const_cat_library = cat_library;        
for(auto& cat: const_cat_library ) 
{
   cat->const_meow(); 
   cat->meow();       // hope to compile error due to invoking non_const method of Cat.
}
const CatLibrary& const_cat_library = cat_library; 

for(const auto& cat: const_cat_library ) 
{
   cat->const_meow(); 
   cat->meow();       // hope to compile error due to invoking non_const method of Cat.
}

我希望我的CatLibrary公开non-const begin()non-const end(),其中客户端可以迭代指向可变Cat的智能指针。并且const begin()const end()返回指向不可变的迭代器的迭代器。

然后当客户端迭代const CatLibrary时,我不担心他可以修改库中Cat的内容。

但是添加到我的成员函数const的{​​{1}}只能将指针限定为const指针,而不是它指向的Cat。

如果没有涉及指针,带有constness的向量会使迭代器指向具有constness的元素。但我希望这个效果也适用于智能指针指向的元素。

我有办法解决我的问题,但我不确定将来使用会发生什么问题。

在const和nonconst

中维护两个向量
begin()

或者是否有其他解决方案来解决向量中智能指针的这种常量问题?

3 个答案:

答案 0 :(得分:1)

在您发布的示例中,不需要const()和end()的const版本。您可以在基于范围的for循环中使用const引用,而不必使用这些函数的const版本,并且您不需要将cat作为const auto&amp;完全可以调用const成员函数。

如果你的cat_library对象本身是const,可能需要const begin()和end(),但是你不能添加这样的项目。

答案 1 :(得分:0)

我实际上考虑将你的抽象转换为封装你的猫集合。迭代器和可变对象是实现细节。

所以用cat编写函数:

PrintCats()
PlayWithCats()

如果您的猫库不知道您要对它们执行的操作,您可以查看传入函数指针。 boost :: function对此有好处。 你有像

这样的功能
void CatLibrary::DoStuffToCats(boost::function<void, (const Cat&)> f))
{
    std::foreach(m_cat_list.begin(), m_cat_list.end(), f);
}

答案 2 :(得分:0)

我用boost :: transform_iterator玩了一下,似乎有可能达到你想要的效果,尽管我觉得结果并不令人满意。我想DanDan建议不向用户公开实现细节可能是正确的方法。

然而,这是我对boost :: transform_iterator的镜头作为参考:

#include <boost/iterator/transform_iterator.hpp>

class CatLibrary
{
public:
  typedef std::vector<std::shared_ptr<Cat>> cat_list_t;
  typedef std::function<const Cat *(const std::shared_ptr<Cat>&)> transform_t;
  typedef boost::transform_iterator<transform_t,
                                    cat_list_t::const_iterator> const_iterator;  

[...]

  const_iterator begin() const {
    return const_iterator(std::begin(m_cat_list),
                          [](const std::shared_ptr<Cat>& c) {
                            return static_cast<const Cat *>(c.get());
                          });
  }
  const_iterator end() const { // same as above ... }
};

使用cat的for循环中const_cat_library的类型现在是const Cat*,因此不允许调用非const函数。使用g++ -c --std=c++11 transform_iterator.cxx -Wall -I$BOOSTINC(gcc版本4.8.1)进行编译会产生以下错误:

error: invalid initialization of non-const reference of type 'const Cat*&' from an rvalue of type 'boost::[...]::reference {aka const Cat*}'
    for(auto& cat: const_cat_library ) {
                   ^
//twice:
error: passing 'const Cat' as 'this' argument of 'void Cat::meow()' discards qualifiers [-fpermissive]
    cat->meow();       // hope to compile error due to invoking non_const method of Cat.
              ^

可能出现的许多问题之一是绕过了shared_ptr,用户可以删除循环内的Cat对象。