Цикл по всем элементам, кроме последнего с использованием диапазона для цикла - PullRequest
0 голосов
/ 30 сентября 2019

Для начала, короткий фрагмент того, чего я хочу достичь:

std::vector<std::string> v{ "one", "two", "three", "four" };
for (const std::string& str : drop_last(v)) {
   cout << str << ' ';
}

Приведенный выше код должен напечатать: «один два три».

Чтобы сделать это возможным,Я реализовал следующую структуру:

template <typename Container>
struct drop_last {
   using const_iterator = typename Container::const_iterator;

   explicit drop_last(const Container& container_) : m_container(container_) {}

   const_iterator begin() {
      return m_container.begin();
   }

   const_iterator end() {
      return m_container.empty() ? m_container.end() : std::prev(m_container.end());
   }
private:
   const Container& m_container;
};

Этот код работает нормально, кроме случаев, когда он работает с временными объектами (в этом случае assert "не может разыменовать инициализированное значением значение итератор карты / набора"):

std::vector<int> return_temporary_vector() {
   vector<int> v;
   // ...
   return v;
}

int main(int argc_, char* argv_[]) {
   for (int value : drop_last(return_temporary_vector())) {
      // ...
   }
   // ...
}

Как мне адаптировать структуру drop_last так, чтобы выполнялись следующие условия:

  • правильная работа с временными объектами
  • по возможности избегать ненужного копирования. То есть простая замена const Container& m_container; на Container m_container; не подходит.

1 Ответ

2 голосов
/ 01 октября 2019

Если вы используете шаблонную функцию со ссылкой для переадресации, вы можете использовать ее для передачи Container или Container& в ваш класс.

#include <vector>
#include <iostream>
#include <type_traits>

template <typename Container>
struct drop_last_impl {
   using const_iterator = typename std::remove_reference_t<Container>::const_iterator;

   explicit drop_last_impl(Container&& container_) : m_container(std::forward<Container>(container_)) {}

   const_iterator begin() {
      return m_container.begin();
   }

   const_iterator end() {
      return m_container.empty() ? m_container.end() : std::prev(m_container.end());
   }
private:
   const Container m_container;
};

template <typename Container>
auto drop_last(Container&& container) {
    return drop_last_impl<Container>(std::forward<Container>(container));
}

std::vector<int> return_temporary_vector() {
   std::vector<int> v{1,2,3};
   // ...
   return v;
}

int main() {
   for (int value : drop_last(return_temporary_vector())) {
      std::cout << value << std::endl;
   }
   std::vector<int> v{1,2,3};
    for (int value : drop_last(v)) {
      std::cout << value << std::endl;
   }
   // ...
}

drop_last_impl теперь перемещается в std::vector<int> если передано значение r, или инициализируется std::vector<int>&, если передано значение l. std::remove_reference_t необходимо, так как если мы установим std::vector<int>&, мы не найдем const_iterator.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...