将单个参数传递给期望迭代器范围的函数

时间:2018-05-04 11:24:49

标签: c++ stl iterator iterator-range

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

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 :(得分: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);
}

答案 1 :(得分:13)

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

function(&value, &value + 1);
  

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

答案 2 :(得分: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

答案 3 :(得分:2)

我想我会分两步完成:

  1. 定义一个带有容器的模板函数的重载,用迭代器版本编写。

  2. 定义一个代理类,将对象引用视为大小为1的数组。

  3. 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 });
    }