Pass a single parameter to a function that expects an iterator range
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.
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:
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.
I think I'd do this in two steps:
Define a overload of the template function that takes a container, written in terms of the iterator version.
Define a proxy class which treats an object reference as an array of size 1.
c++17 example:
#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 });
}
You can also overload your function template function
for a single-element range:
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. 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:
function(&value); // calls overload
Since operator &
may be overloaded, consider also using std::addressof
instead of &
, as already mentioned in this answer.
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