Передайте один параметр функции, которая ожидает диапазон итератора - программирование
Подтвердить что ты не робот

Передайте один параметр функции, которая ожидает диапазон итератора

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

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));

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

Вопрос: Существует ли более эффективный способ создания совместимого с итератором представления единственной переменной?

4b9b3361

Ответ 1

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

function(&value, &value + 1);

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

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

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

Ответ 2

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

function(&value, &value + 1);

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

Ответ 3

Вы также можете перегрузить функцию шаблона 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

Ответ 4

Думаю, я сделаю это в два шага:

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

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

Пример С++ 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 });
}