class Organization {
private:
vector<unique_ptr<Employee>> employees_;
vector<unique_ptr<Item>> items_;
}org;
Мне нужно иметь средство вне класса, чтобы перебирать сотрудников и элементы и вызывать их членов, как показано ниже ...
for(auto const& e : getEmployees()) { e.get()->getName(); }
Но я не могу заставить функцию getEmployees()
возвращать employees_
, так как вектор unique_ptrs не копируется
Итак, в настоящее время у меня есть функция for_each_employee
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(); });
Но мне эта идея не нравится, так как мне придется писать for_each_employee и for_each_item. Имею аналогичные другие классы аналогичной структуры. Так что я напишу много функций типа for_each_XX. Это не то, что я хочу.
Могу ли я иметь общую функцию for_each, которая является друзьями этих классов (например, Организация, содержащая вектор unique_ptrs)?
Как выполнить итерацию (вне класса) частного члена, который является vector<unique_ptr<T>>
или вернуть итераторы к векторам
Вы можете вернуть ссылку на employee_ в getEmployees () для итерации
Да, я согласен, наполовину набрав этот вопрос, я попробовал то же самое и получил ошибку, так как у меня все еще были функции getItems, возвращающие копию. Но я был глуп и не заметил ошибки. Теперь он работает, у меня есть getEmployees и getItems, возвращаемые по ссылкам. Спасибо за ваше время, люди.
Самый простой и вполне читаемый подход - предоставить метод доступа, который возвращает возможно квалифицированную const
ссылку на член данных. Таким образом, вы не пытаетесь копировать не копируемые элементы.
const vector<unique_ptr<Item>>& getItems()
{
return items_;
}
который можно использовать так
for (const auto& item : organizationInstance.getItems())
item->doStuff();
Вы можете вернуть ссылку на employee_ в getEmployees () для итерации
const vector<unique_ptr<Employee>>& getEmployees()
{
return employee_;
}
Вам нужны итераторы. std::vector::begin()
и std::vector::end()
возвращают итераторы к первому и последнему элементам вектора. Тогда вы можете делать такие вещи, как.
for (auto iter = organization.get_employees_begin(); iter != organization.get_employees.end(); ++iter) {
do_something(*iter);
}
Где
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(); }
}
Версии const
возвращают итераторы const
, которые похожи на указатели на const
в том, что они не позволяют изменять вектор. Дополнительным преимуществом этого метода перед возвратом ссылки на вектор является то, что он полностью отделяет реализацию от интерфейса. Если вас это не волнует по какой-либо причине, вы можете использовать это вместо этого.
class Organization {
auto& get_employees() { return employees_; }
const auto& get_employees() const { return employees_; }
}
Это позволит вам использовать все векторные утилиты, но также приведет к поломке всего вашего кода, который полагается на них, если внутренний контейнер изменится с вектора на что-то еще.
В любом случае вы можете не захотеть предоставлять функции, отличные от const
, если вектор не должен изменяться напрямую.
Мне нравится развязка. В этом случае я также могу вернуть пару итераторов. Но использование будет запутанным :) for (auto iter = organization.getEmployees (). First (); iter! = Organization.getEmployees (). Second (); ++ iter) {do_something (* iter); } или я должен привязать его к переменным
Лично я бы не стал использовать парную версию, потому что считаю, что get_employees_begin
намного понятнее любому, кто использовал итераторы.
вы не «полностью» отделили реализацию от интерфейса. Возвращенным итератором по-прежнему является vector<unique_ptr<Employee>>::iterator
, и он будет другого типа, когда вектор будет заменен другим контейнером. Использование auto
в клиентском коде помогает быть независимым от базового контейнера, но auto
помогает точно так же, когда вы возвращаете вектор напрямую.
@ user463035818 Помимо типа возвращаемого значения необходимо учитывать и другие вопросы. Например, вы можете перебирать вектор с помощью operator[]
, но этот код сломается, когда вы измените тип контейнера на несмежный. Возврат итератора передает интерфейс, который вы собираетесь предложить, и усложняет пользователю использование вашего интерфейса непереносимыми способами.
Да, конечно. Я просто наткнулся на вашу формулировку, потому что можно полнота отделить интерфейс от реализации, но, строго говоря, вы этого не делаете
@ user463035818 Как бы вы это сделали?
не уверен, зависит от случая, возможно, вернет EmployeeIterator
, который скрывает тип контейнера от пользователя. С другой стороны, не всегда возможно или необходимо полностью отделить интерфейс от реализации;)
Чтобы разрешить только итерацию, не давая больше векторных операций, вы можете использовать boost::range
auto getItems() {
return boost::make_iterator_range(employees_.begin(), employees_.end());
}
Вот простой класс span. Он похож на gsl::span
. Он представляет собой представление непрерывного буфера элементов T
(как в массиве):
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;
};
просто попросите ваш getEmployees
вернуть span<std::unique_ptr<Employee> const>
.
Это предоставляет все, что нужно вызывающему для эффективного итерации, и не более того.
Более простой альтернативой является возврат std::vector<std::unique_ptr<Employee>> const&
, но это приводит к утечке деталей реализации, которые совершенно не имеют отношения к потребителю getEmployee
.
Почему бы вместо этого не вернуться по ссылке?