Print const and nonconst versions of begin () and end () to iterate a vector element using a smart pointer

//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. } 

I want my CatLibrary to show non-const begin() and non-const end() , in which the client can iterate a smart pointer that points to a mutable Cat. Both const begin() and const end() return an iterator that indicates immutable.

Then, when the client iterates through the const CatLibrary, I would not worry that he could modify the contents of Cat in the library.

But const , added to my begin() member function, only qualifies the pointer as a const pointer, not the Cat that it points to.

Without a pointer, a vector with a constant also makes the iterator a point with an element with a constant. But I want this effect to also apply the element indicated by the smart pointer.

I have an approach to solving my problem, but I'm not sure what problems will arise in the future.

Support two vectors in const and nonconst

 #include <iostream> #include <memory> #include <vector> class Cat {public: void const_meow() const { std::cout << "meow" << std::endl;} void meow() { std::cout << "meow" << std::endl;} }; class CatLibrary {public: void add(std::shared_ptr<Cat> cat) { m_cat_list.push_back(cat); m_cat_const_list.push_back(cat); }; std::vector<std::shared_ptr<Cat>>::const_iterator begin() { return m_cat_list.begin(); } std::vector<std::shared_ptr<const Cat>>::const_iterator begin() const { return m_cat_const_list.begin(); } std::vector<std::shared_ptr<Cat>>::const_iterator end() { return m_cat_list.end(); } std::vector<std::shared_ptr<const Cat>>::const_iterator end() const { return m_cat_const_list.end(); } private: std::vector<std::shared_ptr<Cat>> m_cat_list; std::vector<std::shared_ptr<const Cat>> m_cat_const_list; }; int main() { CatLibrary cat_library; cat_library.add(std::make_shared<Cat>()); cat_library.add(std::make_shared<Cat>()); cat_library.add(std::make_shared<Cat>()); const CatLibrary& const_cat_library = cat_library; for(auto& cat: cat_library) { cat->meow(); } return 0; } 

Or is there another solution to solve this problem with a constant on a smart pointer in a vector?

+6
source share
3 answers

In the above example, there is no need for constant versions of begin () and end (). You can use the const reference in a range-based loop without constant versions of these functions, and you don't need the cat to be const auto & to call the const member function.

There may be a need for const begin () and end () if your cat_library object was const, but then you could not add such elements.

+1
source

I would consider turning your abstraction and encapsulating your cat collection. Iterators and mutable objects are implementation details.

So write the functions in cat:

 PrintCats() PlayWithCats() 

If your cat library is not aware of the actions you want to perform on them, you can look at passing function pointers. boost :: function is good for this. You will have a function like

 void CatLibrary::DoStuffToCats(boost::function<void, (const Cat&)> f)) { std::foreach(m_cat_list.begin(), m_cat_list.end(), f); } 
0
source

I played a little with boost :: transform_iterator, and it seems you can achieve what you want, although I think the result is not satisfactory. I think DanDan's suggestion not to disclose implementation details to the user may be the right way.

However, here is my snapshot for boost :: transform_iterator as a reference:

 #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 ... } }; 

The cat type inside for loops using const_cat_library is now const Cat* , and therefore invocation of non-constant functions is not allowed. Compiling with g++ -c --std=c++11 transform_iterator.cxx -Wall -I$BOOSTINC (gcc version 4.8.1) leads to the following errors:

 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. ^ 

One of the many problems that may arise is that shared_ptr is bypassed and the user can delete the cat object inside the loop.

0
source

Source: https://habr.com/ru/post/973399/


All Articles