можно ли использовать шаблонные методы начала / конца в диапазоне, основанном на циклах - PullRequest
2 голосов
/ 15 октября 2019

Я имею дело с классом, который должен быть итеративным в диапазоне, основанном на циклах, поэтому он определяет класс итератора, метод начала и конца. Теперь в примере, над которым я работаю, эти три шаблона являются шаблонными (у меня есть этот минимальный пример, где параметр шаблона не имеет особого значения, а только имеет шаблон начала и конца):

#include <cstddef>
#include <tuple>
#include <vector>

struct Iterable {
  using Widget = std::tuple<int, float>;
  template <typename access_type>
  struct Iterator {
    Iterable*   m_iterable;
    std::size_t m_pos;
    bool        operator==( const Iterator& other ) { return m_iterable == other.m_iterable && m_pos == other.m_pos; }
    bool        operator!=( const Iterator& other ) { return !( this->operator==( other ) ); }
    access_type operator*() {
      Widget tmp = m_iterable->m_storage[m_pos];
      return std::get<access_type>( tmp );
    }
    Iterator operator++() {
      m_pos++;
      return *this;
    }
  };
  template <typename access_type = int>
  Iterator<access_type> begin() {
    return {this, 0};
  }
  template <typename access_type = int>
  Iterator<access_type> end() {
    return {this, m_storage.size()};
  }
  std::vector<Widget> m_storage;
};

Теперь этоИтерация работает в диапазоне, основанном на цикле

Iterable container;
for (auto element: container) { … }

При этом используются begin<int> и end<int>, что несколько видно из cppinsights (обратите внимание на тип итератора в версии на основе диапазонацикла).

Что мне неясно, есть ли способ указать параметры шаблона для цикла for, отличного от повторения цикла до-++ 11?

for (auto iter = container.begin<float>(); iter != container.end<float>(); ++iter) { … }

РЕДАКТИРОВАТЬ, чтобы уточнить объем обсуждения. Считается, что итерируемый класс уже существует в исходном коде, и я не хочу обсуждать причины его появления в мире. Чужой код просто существует, и мне приходится с ним иметь дело.

см. Также компилятор-проводник

1 Ответ

7 голосов
/ 15 октября 2019

Вам понадобится адаптер, который оборачивает контейнер и предоставляет итераторы, которые вы хотите иметь. Это даст вам класс вроде

template<typename T>
struct Adapter
{
    Iterable & it;
    Adapter(Iterable& it) : it(it) {}
    auto begin() 
    {
        return it.begin<T>();
    }
    auto end() 
    {
        return it.end<T>();
    }  
};

, и вы будете использовать его как

int main()
{
    Iterable container;
    for ( auto element : Adapter<float>{container} ) 
    { 
        static_assert( std::is_same_v<decltype( element ), float> );
    }
}
...