简体   繁体   English

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

[英]Pass a single parameter to a function that expects an iterator range

Consider a function that accepts one or more parameters (eg file names).考虑一个接受一个或多个参数(例如文件名)的函数。 In order to make it versatile, it is advantageous to write it for a general iterator range:为了使其具有通用性,将其编写为通用迭代器范围是有利的:

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

Now we can invoke it in the following way, independently of how we store the arguments:现在我们可以通过以下方式调用它,与我们如何存储参数无关:

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

For example, the STL relies heavily on this paradigm.例如,STL 严重依赖于这种范式。

Now, imagine we want to invoke the function with a single argument that is not stored in a container.现在,假设我们想要使用未存储在容器中的单个参数来调用该函数。 Of course we can write:当然我们可以这样写:

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

But this solution seems clumsy and wasteful to me.但是这个解决方案对我来说似乎很笨拙和浪费。

Question: Is there a better low-overhead way of creating an iterator-range-compatible representation of a single variable?问题:是否有更好的低开销方式来创建单个变量的迭代器范围兼容表示?

You can use pointers, for once:您可以一次性使用指针:

function(&value, &value + 1);

In generic code, std::addressof instead of the unary operator & is somewhat safer, depending on your level of paranoia.在通用代码中, std::addressof而不是一元运算符&更安全,具体取决于您的偏执程度。

You can of course wrap this in an overload for easier use:您当然可以将其包装在重载中以便于使用:

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

You can treat it like an array of one element per [expr.unary.op]/3 :您可以将其视为每个[expr.unary.op]/3一个元素的数组:

function(&value, &value + 1);

For purposes of pointer arithmetic ([expr.add]) and comparison ([expr.rel], [expr.eq]), an object that is not an array element whose address is taken in this way is considered to belong to an array with one element of type T.出于指针运算([expr.add])和比较([expr.rel], [expr.eq])的目的,以这种方式取地址的不是数组元素的对象被认为属于数组带有一个 T 类型的元素。

You can also overload your function template function for a single-element range :您还可以为单元素范围重载函数模板function

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

This way, your original function function remains compatible with iterator ranges.这样,您的原始函数function仍然与迭代器范围兼容。 Note, however, that using this overload with an empty range will result in undefined behavior.但是请注意,将此重载与空范围一起使用将导致未定义的行为。


For a single element, value , you can use the overload above:对于单个元素value ,您可以使用上面的重载:

function(&value); // calls overload

Since operator & may be overloaded, consider also using std::addressof instead of & , as already mentioned in this answer .由于运算符&可能会被重载,因此还可以考虑使用std::addressof而不是& ,如本答案中已经提到的。


For a range consisting of a single element, you can use the overload above as well, which only needs a single iterator instead of an iterator pair:对于由单个元素组成的范围,您也可以使用上面的重载,它只需要一个迭代器而不是迭代器对:

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

I think I'd do this in two steps:我想我会分两步做到这一点:

  1. Define a overload of the template function that takes a container, written in terms of the iterator version.定义采用容器的模板函数的重载,根据迭代器版本编写。

  2. Define a proxy class which treats an object reference as an array of size 1.定义一个代理类,它将对象引用视为大小为 1 的数组。

c++17 example: 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 });
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM