将参数传递给需要迭代器范围的函数

问题描述 投票:21回答:4

考虑一个接受一个或多个参数(例如文件名)的函数。为了使其通用,为一般的迭代器范围编写它是有利的:

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

但是这个解决方案对我来说似乎很笨拙而且浪费。

问题:是否有更好的低开销方法来创建单个变量的迭代器范围兼容表示?

c++ stl iterator iterator-range
4个回答
33
投票

您可以一次使用指针:

function(&value, &value + 1);

在通用代码中,根据您的偏执程度,std::addressof代替一元运算符&会更安全一些。

您当然可以将其包装在重载中以便于使用:

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

13
投票

您可以将其视为每个[expr.unary.op]/3包含一个元素的数组:

function(&value, &value + 1);

出于指针算术([expr.add])和比较([expr.rel],[expr.eq])的目的,不是以这种方式获取地址的数组元素的对象被认为属于具有一个类型为T的元素的数组。


6
投票

您也可以重载您的功能模板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而不是&,如this answer中已提及。


对于由单个元素组成的范围,您也可以使用上面的重载,它只需要一个迭代器而不是一对迭代器:

const int value = 5;
std::vector<int> vec(1, value); // single-element collection
function(std::begin(vec)); // <-- calls overload

2
投票

我想我可以分两个步骤进行:

  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 });
}
© www.soinside.com 2019 - 2024. All rights reserved.