Передайте единственный параметр функции, которая ожидает диапазон итератора - PullRequest
0 голосов
/ 04 мая 2018

Рассмотрим функцию, которая принимает один или несколько параметров (например, имена файлов). Чтобы сделать его универсальным, полезно написать его для общего диапазона итераторов:

template<class Iter>
void function(Iter first, Iter last)
{
  // do something
}

Теперь мы можем вызвать его следующим образом, независимо от того, как мы храним аргументы:

WhateverContainer container;
function(std::begin(container), std::end(container));

Например, STL в значительной степени опирается на эту парадигму.

Теперь представьте, что мы хотим вызвать функцию с одним аргументом, который не хранится в контейнере. Конечно, мы можем написать:

const int value = 5;
std::vector<int> vec(1, value);
function(std::begin(vec), std::end(vec));

Но это решение кажется мне неуклюжим и расточительным.

Вопрос: Есть ли лучший способ с минимальными издержками для создания совместимого с диапазоном итератора представления одной переменной?

Ответы [ 4 ]

0 голосов
/ 04 мая 2018

Думаю, я бы сделал это в два этапа:

  1. Определить перегрузку функции шаблона, которая принимает контейнер, записанную в терминах версии итератора.

  2. Определить прокси-класс, который обрабатывает ссылку на объект как массив размером 1.

c ++ 17 пример:

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

// proxy object
template<class T>
struct object_as_container
{
    using value_type = T;
    using iterator = T*;
    using const_iterator = std::add_const_t<T>;

    object_as_container(value_type& val) : object_(val) {}

    const_iterator begin() const { return std::addressof(object_); }
    iterator begin() { return std::addressof(object_); }

    const_iterator end() const { return std::next(begin()); }
    iterator end() { return std::next(begin()); }

private:
    value_type& object_;
};

// our function in terms of iterators    
template<class Iter> void func(Iter first, Iter last)
{
    while(first != last)
    {
        std::cout << *first++;
    }
}

// our function in terms of containers
template<class Container> void func(Container&& cont)
{
    func(cont.begin(), cont.end());
}

int main()
{
    const int value = 5;
    func(object_as_container(value));
    func(std::vector { 1,2,3,4,5 });
}
0 голосов
/ 04 мая 2018

Вы можете рассматривать его как массив из одного элемента на [expr.unary.op] / 3 :

function(&value, &value + 1);

В целях арифметики указателей ([expr.add]) и сравнения ([expr.rel], [expr.eq]) объект, который не является элементом массива, адрес которого взят таким образом, считается принадлежащим в массив с одним элементом типа T.

0 голосов
/ 04 мая 2018

Вы также можете перегрузить ваш шаблон функции function для одноэлементного диапазона :

template<typename Iter>
void function(Iter first) {
    return function(first, std::next(first)); // calls your original function
}

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


Для одного элемента, value, вы можете использовать приведенную выше перегрузку:

function(&value); // calls overload

Поскольку оператор & может быть перегружен, рассмотрите возможность использования std::addressof вместо &, как уже упоминалось в этого ответа .


Для диапазона, состоящего из одного элемента, вы также можете использовать приведенную выше перегрузку, для которой нужен только один итератор вместо пары итераторов:

const int value = 5;
std::vector<int> vec(1, value); // single-element collection
function(std::begin(vec)); // <-- calls overload
0 голосов
/ 04 мая 2018

Вы можете использовать указатели, за один раз:

function(&value, &value + 1);

В общем коде std::addressof вместо унарного оператора & несколько безопаснее, в зависимости от вашего уровня паранойи.

Вы можете, конечно, обернуть это в перегрузку для более легкого использования:

template <class T>
decltype(auto) function (T &&e) {
    auto p = std::addressof(e);
    return function(p, p + 1);
}
...