Logesh G
Logesh G

Reputation: 640

How to iterate (outside a class) a private member which is a vector<unique_ptr<T>>

class Organization {
 private:
  vector<unique_ptr<Employee>> employees_;
  vector<unique_ptr<Item>> items_;
}org;

I need to have a facility outside the class to iterate over the employees and items and call their members, like the following...

for(auto const& e : getEmployees()) { e.get()->getName(); } 

But I cannot make getEmployees() function to return employees_, as vector of unique_ptrs is not copyable

So currently I'm having a for_each_employee function

template<class callable>
void Organization::for_each_employee(callable f) {
     for(auto const& e : employees_) {
        f(e.get());
   }
}

// And I use it like,
org.for_each_employee([](Employee* e){ e->getName(); });

But I do not like this idea as I'll have to write for_each_employee and for_each_item. I have similar other classes of the similar structure. So I will end up writing lot of for_each_XX type functions. This is not what I want.

Can I have generic for_each function that are friends of these classes (like Organization that contain vector of unique_ptrs)?

How to iterate (outside a class) a private member which is a vector<unique_ptr<T>>

Upvotes: 1

Views: 1467

Answers (5)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275405

Here is a simple span class. It is similar to gsl::span. It represents a view into a contiguous buffer of T elements (like in an array):

template<class T>
struct span {
  T* begin() const { return b; }
  T* end() const { return e; }
  T* data() const { return begin(); }
  std::size_t size() const { return end()-begin(); }
  bool empty() const { return size()==0; }

  span( T* s, T* f ):b(s),e(f) {}
  span( T* s, std::size_t length ):span(s, s+length){}

  span() = default;

  template<class A>
  span( std::vector<T, A>& v ):
    span(v.data(), v.length() )
  {}
  template<class A>
  span( std::vector<std::remove_const_t<T>, A> const& v ):
    span(v.data(), v.length() )
  {}
  template<std::size_t N>
  span( T(& arr)[N] ):
    span(arr, N)
  {}
  template<std::size_t N>
  span( std::array<T, N>& arr ):
    span(arr.data(), N)
  {}
  template<std::size_t N>
  span( std::array<std::remove_const_t<T>, N> const& arr ):
    span(arr.data(), N)
  {}

private:
  T* b = 0;
  T* e = 0;
};

simply have your getEmployees return a span<std::unique_ptr<Employee> const>.

This exposes everything the caller needs to iterate over it efficiently, and no more.

The simpler alternative is to return a std::vector<std::unique_ptr<Employee>> const&, but that leaks implementation details that are utterly irrelevant to the consumer of getEmployee.

Upvotes: 1

bartop
bartop

Reputation: 10315

To allow only iterating, not giving any more vector-specific operations You can use boost::range

auto getItems() {
    return boost::make_iterator_range(employees_.begin(), employees_.end());
}

Upvotes: 1

patatahooligan
patatahooligan

Reputation: 3321

What you're looking for is iterators. std::vector::begin() and std::vector::end() return iterators to the first and to the one-past-the-end elements of the vector. Then you can do stuff like.

for (auto iter = organization.get_employees_begin(); iter != organization.get_employees.end(); ++iter) {
    do_something(*iter);
}

Where

class Organization {
    auto get_employees_begin() { return employees_.begin(); }
    auto get_employees_begin() const { return employees_.begin(); }

    auto get_employees_end() { return employees_.end(); }
    auto get_employees_end() const { return employees_.end(); }
}

The const versions return const iterators, which are similar to pointers-to-const in that they don't allow modification of the vector. The added benefit of this method over returning a reference to the vector is that it completely decouples the implementation from the interface. If you don't care about that for whatever reason, you can use this instead.

class Organization {
    auto& get_employees() { return employees_; }
    const auto& get_employees() const { return employees_; }
}

This will allow you to use all vector utilities but it will also make all your code that relies on them break if the internal container changes from a vector to something else.

In any case, you might not want to provide the non-const functions if the vector should not be modified directly.

Upvotes: 2

Eklavya
Eklavya

Reputation: 18440

You can return reference of employee_ in getEmployees() to iterate

const vector<unique_ptr<Employee>>& getEmployees()
{
    return employee_;
}

Upvotes: 3

lubgr
lubgr

Reputation: 38287

The simplest and quite readable approach is to provide an access method that returns a possibly const-qualified reference to the data member. This way, you don't try to copy non-copyable members.

const vector<unique_ptr<Item>>& getItems()
{
    return items_;
}

which can be used like this

for (const auto& item : organizationInstance.getItems())
    item->doStuff();

Upvotes: 3

Related Questions