Reputation: 640
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
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
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
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
Reputation: 18440
You can return reference of employee_ in getEmployees() to iterate
const vector<unique_ptr<Employee>>& getEmployees()
{
return employee_;
}
Upvotes: 3
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