将单个参数传递给需要迭代器范围的函数
Pass a single parameter to a function that expects an 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));
但这个解决方案对我来说显得笨拙和浪费。
问题:是否有更好的低开销方法来创建单个变量的迭代器范围兼容表示?
你可以使用指针一次:
function(&value, &value + 1);
在通用代码中,std::addressof
而不是一元运算符 &
更安全一些,具体取决于您的偏执程度。
您当然可以将其包装在重载中以便于使用:
template <class T>
decltype(auto) function (T &&e) {
auto p = std::addressof(e);
return function(p, p + 1);
}
您可以将其视为每个元素一个的数组 [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.
您还可以重载您的函数模板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
我想我会分两步完成:
定义采用容器的模板函数重载,根据迭代器版本编写。
定义一个代理 class 将对象引用视为大小为 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 });
}
考虑一个接受一个或多个参数(例如文件名)的函数。为了使其具有通用性,将其编写为通用迭代器范围是有利的:
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));
但这个解决方案对我来说显得笨拙和浪费。
问题:是否有更好的低开销方法来创建单个变量的迭代器范围兼容表示?
你可以使用指针一次:
function(&value, &value + 1);
在通用代码中,std::addressof
而不是一元运算符 &
更安全一些,具体取决于您的偏执程度。
您当然可以将其包装在重载中以便于使用:
template <class T>
decltype(auto) function (T &&e) {
auto p = std::addressof(e);
return function(p, p + 1);
}
您可以将其视为每个元素一个的数组 [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.
您还可以重载您的函数模板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
我想我会分两步完成:
定义采用容器的模板函数重载,根据迭代器版本编写。
定义一个代理 class 将对象引用视为大小为 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 });
}