std :: array :: iterator, который игнорирует размер шаблона - PullRequest
1 голос
/ 04 мая 2020

Я реализую функцию, которая хочет l oop для нескольких элементов в массиве std ::, но мне все равно, какой длины массив std ::. Поэтому я подумал о следующей функции:

#include <stdio.h>
#include <array>
#include <iterator>

void foo(std::array<bool,0>::const_iterator begin, std::array<bool,0>::const_iterator end)
{
    printf("iterator");
}

int main()
{
    std::array<bool, 25> one;
    std::array<bool, 33> two;

    foo(one.cbegin(), one.cend());
    foo(two.cbegin(), two.cend());
}

Я вполне согласен с этим, за исключением std::array<bool,0>. У меня вопрос, есть ли другой способ указать итератор, необходимый для этой функции?


Обновление

Есть некоторые вещи, которые я должен упомянуть. Конечно, этот код является частью большей области, и я пытался скрыть как можно больше деталей.

  1. Я хочу убедиться, что используемый итератор имеет bool s.
  2. Я использую C ++ 14
  3. Функция является частью интерфейса класса, и я хочу иметь возможность обрабатывать массивы нескольких размеров. Я не хочу мешать разработчикам интерфейса точно знать, какой размер массива.
class MyInterface
{
public:
    virtual foo(std::array<bool,0>::const_iterator begin, std::array<bool,0>::const_iterator end) = 0;

    ~MyInterface() = default;
};

Я вспомнил, что виртуальные функции не могут быть шаблонизированы. Это означает, что мне придется шаблонизировать весь мой интерфейс, и это точно потеряло бы смысл, почему я пытался это сделать в первую очередь.

Ответы [ 4 ]

3 голосов
/ 04 мая 2020

Просто используйте span:

#include <array>
#include <span>

class MyInterface {
public:
    virtual void foo(std::span<bool> barr) = 0;

    // interface destructors should be virtual
    virtual ~MyInterface() = default;
};

void bar(MyInterface& interface) {
    std::array<bool, 42> arr;
    interface.foo(arr);
}

Если у вас нет доступа к компилятору C ++ 20, вы можете использовать gsl::span из gsl вместо.

3 голосов
/ 04 мая 2020

Вы можете просто сделать его шаблоном функции как

template <typename I>
void foo(I begin, I end)
{
    std::cout << "iterator";
}

Вам не нужно заботиться о типе контейнера (и размере), вы можете передать итераторы std::array, std::vector и std::string и т. Д., Даже необработанные указатели (что также удовлетворяет требованиям итератора).

1 голос
/ 04 мая 2020

Если функция принимает два итератора, когда ссылающийся std :: array избыточен. Просто объявите функцию как

template <class Iterator>
void foo( Iterator first, Iterator last );

В объявлении вы можете назвать итератор, соответствующий используемому типу итератора в функции, например,

template <class ForwardIterator>
void foo( ForwardIterator first, ForwardIterator last );

Или вместо имени ForwardIterator вы можете использовать имя BidirectionalIterator или RandomAccessIterator для самодокументирования.

Если вам нужно знать тип значения итератора, вы можете использовать разные подходы. Например,

template <class Iterator>
void foo( Iterator first, Iterator last )
{
    using value_type = typename std::iterator_traits<Iterator>::value_type;
    if ( first != last )
    {
         value_type item1 = *first;
         // or
         auto item2 = *first;
         //
         const auto &item3 = *first;
         //...             
    }
}

В этом случае у вас будет гибкое определение функции. Например, если в будущем вы измените std::array<N, bool> на std::vector<bool>, функция будет работать как обычно.

1 голос
/ 04 мая 2020

Используйте шаблон:

template <size_t N>
void foo(std::array<bool,N>::const_iterator begin, std::array<bool,N>::const_iterator end)
{
    printf("iterator");
}

И теперь, пока оба итератора приходят из массива размером N, эта функция будет работать.


Если вы хотите принимать итераторы из разных размеров массивов, вам просто нужно добавить еще один параметр шаблона для второго итератора, например

template <size_t N, size_t M>
void foo(std::array<bool,N>::const_iterator begin, std::array<bool,M>::const_iterator end)
{
    printf("iterator");
}
...