Is there a simpler way to write this, e.g. by using an STL or boost algorithm?
std::vector<int> v { 0, 1, 2, 3 }; // any generic STL container
std::vector<int> result;
std::transform(v.begin(), v.end() - 1, // (0, 1, 2)
v.begin() + 1, // (1, 2, 3)
std::back_inserter(result),
[](int a, int b){ return a + b; }); // any binary function
// result == { 1, 3, 5 }
I propose using a for loop:
for(std::vector::size_type i = 0; i < v.size() - 1; i++)
result.push_back(v[i] + v[i+1])
A more generic loop for bidirectional iterators:
// let begin and end be iterators to corresponding position
// let out be an output iterator
// let fun be a binary function
for (auto it = begin, end_it = std::prev(end); it != end_it; ++it)
*out++ = fun(*it, *std::next(it));
We can go a bit further and write a loop for forward iterators:
if(begin != end) {
for (auto curr = begin,
nxt = std::next(begin); nxt != end; ++curr, ++nxt) {
*out++ = fun(*curr, *nxt);
}
}
Finally, and algorithm for input iterators. However, this one requires that the value type is copyable.
if(begin != end) {
auto left = *begin;
for (auto it = std::next(begin); it != end; ++it) {
auto right = *it;
*out++ = fun(left, right);
left = right;
}
}
The binary version of std::transform can be used.
The std::adjacent_find/std::adjacent_difference algorithms can be abused.
std::adjacent_difference is for exactly this, but as you mentioned, it copies the first element to the result, which you don't want.
Using Boost.Iterator, it's pretty easy to make a back_inserter which throws away the first element.
#include <boost/function_output_iterator.hpp>
template <class Container>
auto mybackinsrtr(Container& cont) {
// Throw away the first element
return boost::make_function_output_iterator(
[&cont](auto i) -> void {
static bool first = true;
if (first)
first = false;
else
cont.push_back(i);
});
}
Then you can #include <boost/range/numeric.hpp> and do this:
std::vector<int> v { 0, 1, 2, 3 }; // any generic STL container
std::vector<int> result;
boost::adjacent_difference(v, mybackinsrtr(result), std::plus<>{}); // any binary function
See it on ideone
When you want your binary function to return a different type (such as a string), the above solution won't work because, even though the insertion cont.push_back(i) is never called for the first copied element, it still must be compiled and it won't go.
So, you can instead make a back_inserter that ignores any elements of a different type than go in the container. This will ignore the first, copied, element, and accept the rest.
template <class Container>
struct ignore_insert {
// Ignore any insertions that don't match container's type
Container& cont;
ignore_insert(Container& c) : cont(c) {}
void operator() (typename Container::value_type i) {
cont.push_back(i);
}
template <typename T>
void operator() (T) {}
};
template <class Container>
auto ignoreinsrtr(Container& cont) {
return boost::make_function_output_iterator(ignore_insert<Container>{cont});
}
Then you can use it similarly.
std::vector<int> v { 0, 1, 2, 3 }; // any generic STL container
std::vector<std::string> result;
boost::adjacent_difference(v, ignoreinsrtr(result), [](int a, int b){ return std::to_string(a+b); });
On ideone
I would write your own algorithm to apply a functor to each pair of elements in the container.
(Shameless blurb) In my ACCU presentation this year, "STL Algorithms – How to Use Them and How to Write Your Own", showed how to write one like this. I called it adjacent_pair (about 25:00 into the video)
template <typename ForwardIterator, typename Func>
void adjacent_pair(ForwardIterator first, ForwardIterator last, Func f)
{
if (first != last)
{
ForwardIterator trailer = first;
++first;
for (; first != last; ++first, ++trailer)
f(*trailer, *first);
}
}
Stephan T. Lavavej has written a nice adjacent_iterator class here:
How do I loop over consecutive pairs in an STL container using range-based loop syntax?
This could also be used here.
Related
I need an STL algorithm that takes a predicate and a collection and returns true if one and only one member of the collection satisfies the predicate, otherwise returns false.
How would I do this using STL algorithms?
E.g., to replace the following with STL algorithm code to express the same return value.
int count = 0;
for( auto itr = c.begin(); itr != c.end(); ++itr ) {
if ( predicate( *itr ) ) {
if ( ++count > 1 ) {
break;
}
}
}
return 1 == count;
Two things come to my mind:
std::count_if and then compare the result to 1.
To avoid traversing the whole container in case eg the first two elements already match the predicate I would use two calls looking for matching elements. Something along the line of
auto it = std::find_if(begin,end,predicate);
if (it == end) return false;
++it;
return std::none_of(it,end,predicate);
Or if you prefer it more compact:
auto it = std::find_if(begin,end,predicate);
return (it != end) && std::none_of(std::next(it),end,predicate);
Credits goes to Remy Lebeau for compacting, Deduplicator for debracketing and Blastfurnance for realizing that we can also use none_of the std algorithms.
You can use std::count_if† to count and return if it is one.
For example:
#include <iostream>
#include <algorithm> // std::count_if
#include <vector> // std::vector
#include <ios> // std::boolalpha
template<class Iterator, class UnaryPredicate>
constexpr bool is_count_one(Iterator begin, const Iterator end, UnaryPredicate pred)
{
return std::count_if(begin, end, pred) == 1;
}
int main()
{
std::vector<int> vec{ 2, 4, 3 };
// true: if only one Odd element present in the container
std::cout << std::boolalpha
<< is_count_one(vec.cbegin(), vec.cend(),
[](const int ele) constexpr noexcept -> bool { return ele & 1; });
return 0;
}
†Update: However, std::count_if counts entire element in the container, which is not good as the algorithm given in the question. The best approach using the standard algorithm collections has been mentioned in #formerlyknownas_463035818 's answer.
That being said, OP's approach is also good as the above mentioned best standard approach, where a short-circuiting happens when count reaches 2. If someone is interested in a non-standard algorithm template function for OP's approach, here is it.
#include <iostream>
#include <vector> // std::vector
#include <ios> // std::boolalpha
#include <iterator> // std::iterator_traits
template<class Iterator, class UnaryPredicate>
bool is_count_one(Iterator begin, const Iterator end, UnaryPredicate pred)
{
typename std::iterator_traits<Iterator>::difference_type count{ 0 };
for (; begin != end; ++begin) {
if (pred(*begin) && ++count > 1) return false;
}
return count == 1;
}
int main()
{
std::vector<int> vec{ 2, 3, 4, 2 };
// true: if only one Odd element present in the container
std::cout << std::boolalpha
<< is_count_one(vec.cbegin(), vec.cend(),
[](const int ele) constexpr noexcept -> bool { return ele & 1; });
return 0;
}
Now that can be generalized, by providing one more parameter, the number of N element(s) has/ have to be found in the container.
template<typename Iterator>
using diff_type = typename std::iterator_traits<Iterator>::difference_type;
template<class Iterator, class UnaryPredicate>
bool has_exactly_n(Iterator begin, const Iterator end, UnaryPredicate pred, diff_type<Iterator> N = 1)
{
diff_type<Iterator> count{ 0 };
for (; begin != end; ++begin) {
if (pred(*begin) && ++count > N) return false;
}
return count == N;
}
Starting from formerlyknownas_463035818's answer, this can be generalized to seeing if a container has exactly n items that satisfy a predicate. Why? Because this is C++ and we're not satisfied until we can read email at compile time.
template<typename Iterator, typename Predicate>
bool has_exactly_n(Iterator begin, Iterator end, size_t count, Predicate predicate)
{
if(count == 0)
{
return std::none_of(begin, end, predicate);
}
else
{
auto iter = std::find_if(begin, end, predicate);
return (iter != end) && has_exactly_n(std::next(iter), end, count - 1, predicate);
}
}
Using std::not_fn to negate a predicate
As the core of the algorithm of this question (as has been elegantly covered by combining std::find_if and std::none_of in the accepted answer), with short-circuiting upon failure, is to scan a container for a unary predicate and, when met, continue scanning the rest of the container for the negation of the predicate, I will mention also the negator std::not_fn introduced in C++17, replacing the less useful std::not1 and std::not2 constructs.
We may use std::not_fn to implement the same predicate logic as the accepted answer (std::find_if conditionally followed by std::none_of), but with somewhat different semantics, replacing the latter step (std::none_of) with std::all_of over the negation of the unary predicate used in the first step (std::find_if). E.g.:
// C++17
#include <algorithm> // std::find_if
#include <functional> // std::not_fn
#include <ios> // std::boolalpha
#include <iostream>
#include <iterator> // std::next
#include <vector>
template <class InputIt, class UnaryPredicate>
constexpr bool one_of(InputIt first, InputIt last, UnaryPredicate p) {
auto it = std::find_if(first, last, p);
return (it != last) && std::all_of(std::next(it), last, std::not_fn(p));
}
int main() {
const std::vector<int> v{1, 3, 5, 6, 7};
std::cout << std::boolalpha << "Exactly one even number : "
<< one_of(v.begin(), v.end(), [](const int n) {
return n % 2 == 0;
}); // Exactly one even number : true
}
A parameter pack approach for static size containers
As I’ve already limited this answer to C++14 (and beyond), I’ll include an alternative approach for static size containers (here applied for std::array, specifically), making use of std::index_sequence combined with parameter pack expansion:
#include <array>
#include <ios> // std::boolalpha
#include <iostream>
#include <utility> // std::(make_)index_sequence
namespace detail {
template <typename Array, typename UnaryPredicate, std::size_t... I>
bool one_of_impl(const Array& arr, const UnaryPredicate& p,
std::index_sequence<I...>) {
bool found = false;
auto keep_searching = [&](const int n){
const bool p_res = found != p(n);
found = found || p_res;
return !found || p_res;
};
return (keep_searching(arr[I]) && ...) && found;
}
} // namespace detail
template <typename T, typename UnaryPredicate, std::size_t N,
typename Indices = std::make_index_sequence<N>>
auto one_of(const std::array<T, N>& arr,
const UnaryPredicate& p) {
return detail::one_of_impl(arr, p, Indices{});
}
int main() {
const std::array<int, 5> a{1, 3, 5, 6, 7};
std::cout << std::boolalpha << "Exactly one even number : "
<< one_of(a, [](const int n) {
return n % 2 == 0;
}); // Exactly one even number : true
}
This will also short-circuit upon early failure (“found more than one”), but will contain a few more simple boolean comparisons than in the approach above.
However, note that this approach could have its draw-backs, particularly for optimized code for container inputs with many elements, as is pointed out by #PeterCordes in a comment below. Citing the comment (as comments are not guaranteed to persist over time):
Just because the size is static doesn't mean that fully unrolling the loop with templates is a good idea. In the resulting asm, this needs a branch every iteration anyway to stop on found, so that might as well be a loop-branch. CPUs are good at running loops (code caches, loopback buffers). Compilers will fully unroll static-sized loops based on heuristics, but probably won't roll this back up if a is huge. So your first one_of implementation has the best of both worlds already, assuming a normal modern compiler like gcc or clang, or maybe MSVC
I want to write a function that takes an std container beginning and ending to add all values in the container to the third argument. For example, if I have
std::vector<int> my_ints{ 1, 2, 3 };
int sum_int = accumulate(my_ints.cbegin(), my_ints.cend(), 10); //should return 16.
I wanted to generalize this function in a way that it can work with any std containers. How can I write a template that can access to the iterators of the elements?
template<typename It, typename T>
T accumulate(It begin, It end, T x)
{
for (It i = begin; i != end; ++i)
{
x = x + i;
}
return x;
}
This is what I have at the moment. But, it does not compile because x and i are not the same type.
You should reach for std::for_each to do this rather than writing your own function. It will accept any container and any range of values.
If you want to write your own the look into the for-range statement:
template <typename C>
auto accumulate(const C& c) {
typename C::value_type x { };
for (auto value : c) {
x += value;
}
return x;
}
std::vector<int> my_ints{ 1, 2, 3 };
int sum = accumulate(my_ints);
As Peter mentioned in a comment, you are trying to add the actual iterator to x instead of the value it's pointing at. You need to dereference the iterator to get the value:
template<typename It, typename T>
T accumulate(It begin, It end, T x) {
// there's no need create a new iterator (i) here, use the one you already have (begin):
for(; begin != end; ++begin)
x += *begin; // use * to dereference the iterator
return x;
}
I want to find the minimum element in an array, but if the minimum element appears more than once, then I want the last occurrence of the element. I used std::min_element() with my comp() function.
vector<int>::iterator it=min_element(input.begin(), input.end(),comp);
cout << *it << endl;
cout << distance(input.begin(), it);
bool comp(int a, int b) {
if (a <= b)
return true;
else
return false;
}
This code is giving an error saying invalid comparator on input 3 3 4.
Give min_element reverse iterators instead:
vector<int>::reverse_iterator it=min_element(input.rbegin(), input.rend(),comp);
Then convert it back to a "normal" iterator iff you need to.
And don't forget to correct your comparator; it needs to be < not <=.
You might abuse of std::minmax_element which returns the last biggest element contrary to std::max_element:
auto last_min_it = std::minmax_element(input.begin(), input.end(), std::greater<>{}).second;
I would probably use reverse iterator with std::min_element, though:
auto min_rev_it = std::min_element(input.rbegin(), input.rend());
If your data are stored in a vector, then the use of a reverse iterator should suffice, as already suggested.
More generally, the Standard Library does not provide a min_element_last function, as also commented in 1. In this respect, a possible implementation of min_element_last may read:
template <typename I>
using ValueType = typename std::iterator_traits<I>::value_type;
template <typename I, typename R>
// I models ForwardIterator
// R models StrictWeakOrdering on ValueType<I>
I min_element_last(I first, I last, R cmp) {
if (first == last) return last;
I curr = first;
++first;
while (first != last) {
if (!cmp(*curr, *first)) {
curr = first;
}
++first;
}
return curr;
}
template <typename I>
// I models ForwardIterator
// ValueType<I> models TotallyOrdered
I min_element_last(I first, I last) {
using T = ValueType<I>;
return min_element_last(first, last, std::less<T>());
}
The advantage would be the possibility of using min_element_last also with iterators that only model the ForwardIterator concept.
I have binary relation on some type T induced by a function equivalent:
bool equivalent(T const& a, T const& b); // returns true if a and b are equivalent
It has the properties that
equivalent(a, a) == true
and
equivalent(a, b) == equivalent(b, a)
for all a, b.
For a given collection of elements of type T, I want to remove all but the first occurrence of each equivalence class. I have come up with the following Code but was wandering:
Is there is a solution without an explicit loop?
std::vector<T> filter_all_but_one_for_each_set_of_equivalent_T(std::vector<T> const& ts) {
std::vector<T> result;
for (auto iter = ts.begin(); iter != ts.end(); ++iter) {
auto const& elem = *iter;
bool has_equivalent_element_at_earlier_position = std::any_of(
ts.begin(),
iter,
&equivalent
);
if (not has_equivalent_element_at_earlier_position) {
result.push_back(routing_pin);
}
}
return result;
}
Update
As far as I understand std::unique won't do because my type T is not sortable. And because I only have C++11 in my case, but I would be interested in other options too for education.
Here's a way that only has one very simple loop:
First define our class, which I'll call A instead of T because T is typically used for templates:
class A{
public:
explicit A(int _i) : i(_i){};
int get() const{return i;}
private:
int i;
};
And then our equivalent function just compares the integers for equality:
bool equivalent(A const& a, A const& b){return a.get() == b.get();}
next I'll define the filtering function.
The idea here is to take advantage of std::remove to do the looping and erasing efficiently for us (it typically swaps elements to the end so that you are not shifting the vector for each removal).
We start by removing everything that matches the first element, then afterwards remove everything that matches the second element (which is guaranteed != to the first element now), and so on.
std::vector<A> filter_all_but_one_for_each_set_of_equivalent_A(std::vector<A> as) {
for(size_t i = 1; i < as.size(); ++i){
as.erase(std::remove_if(as.begin() + i, as.end(), [&as, i](const A& next){return equivalent(as[i-1], next);}), as.end());
}
return as;
}
Demo
Edit: As Richard Hodges mentioned, it is possible to delay any erasing until the very end. I couldn't make it look as beautiful though:
std::vector<A> filter_all_but_one_for_each_set_of_equivalent_A(std::vector<A> as) {
auto end = as.end();
for(size_t i = 1; i < std::distance(as.begin(), end); ++i){
end = std::remove_if(as.begin() + i, end, [&as, i](const A& next){return equivalent(as[i-1], next);});
}
as.erase(end, as.end());
return as;
}
Demo 2
Expanding on my comment in AndyG's answer:
template<class T, class A, class Equivalent>
auto deduplicated2(std::vector<T, A> vec, Equivalent&& equivalent) -> std::vector<T, A>
{
auto current = std::begin(vec);
// current 'last of retained sequence'
auto last = std::end(vec);
while (current != last)
{
// define a predicate which checks for equivalence to current
auto same = [&](T const& x) -> bool
{
return equivalent(*current, x);
};
// move non-equivalent items to end of sequence
// return new 'end of valid sequence'
last = std::remove_if(std::next(current), last, same);
}
// erase all items beyond the 'end of valid sequence'
vec.erase(last, std::end(vec));
return vec;
}
Credit to AndyG please.
For very large vectors where T is hashable, we can aim for an O(n) solution:
template<class T, class A, class Equivalent>
auto deduplicated(std::vector<T, A> const& vec, Equivalent&& equivalent) -> std::vector<T, A>
{
auto seen = std::unordered_set<T, std::hash<T>, Equivalent>(vec.size(), std::hash<T>(), std::forward<Equivalent>(equivalent));
auto result = std::vector<T, A>();
result.resize(vec.size());
auto current = std::begin(vec);
while (current != std::end(vec))
{
if (seen.insert(*current).second)
{
result.push_back(*current);
}
}
return result;
}
Finally, revisiting the first solution and refactoring into sub-concerns (I can't help myself):
// in-place de-duplication of sequence, similar interface to remove_if
template<class Iter, class Equivalent>
Iter inplace_deduplicate_sequence(Iter first, Iter last, Equivalent&& equivalent)
{
while (first != last)
{
// define a predicate which checks for equivalence to current
using value_type = typename std::iterator_traits<Iter>::value_type;
auto same = [&](value_type const& x) -> bool
{
return equivalent(*first, x);
};
// move non-equivalent items to end of sequence
// return new 'end of valid sequence'
last = std::remove_if(std::next(first), last, same);
}
return last;
}
// in-place de-duplication on while vector, including container truncation
template<class T, class A, class Equivalent>
void inplace_deduplicate(std::vector<T, A>& vec, Equivalent&& equivalent)
{
vec.erase(inplace_deduplicate_sequence(vec.begin(),
vec.end(),
std::forward<Equivalent>(equivalent)),
vec.end());
}
// non-destructive version
template<class T, class A, class Equivalent>
auto deduplicated2(std::vector<T, A> vec, Equivalent&& equivalent) -> std::vector<T, A>
{
inplace_deduplicate(vec, std::forward<Equivalent>(equivalent));
return vec;
}
You can try this one. The trick here is to obtain the index while inside predicate.
std::vector<T> output;
std::copy_if(
input.begin(), input.end(),
std::back_inserter(output),
[&](const T& x) {
size_t index = &x - &input[0];
return find_if(
input.begin(), input.begin() + index, x,
[&x](const T& y) {
return equivalent(x, y);
}) == input.begin() + index;
});
Since performance is not an issue, you can use std::accumulate to scan through the elements and add them to an accumulator vector xs if there is not already
an equaivalent element in xs.
With this you don't need any hand-written raw loops at all.
std::vector<A> filter_all_but_one_for_each_set_of_equivalent_A(std::vector<A> as) {
return std::accumulate(as.begin(), as.end(),
std::vector<A>{}, [](std::vector<A> xs, A const& x) {
if ( std::find_if(xs.begin(), xs.end(), [x](A const& y) {return equivalent(x,y);}) == xs.end() ) {
xs.push_back(x);
}
return xs;
});
}
With two helper functions this becomes actually readable:
bool contains_equivalent(std::vector<A> const& xs, A const& x) {
return std::find_if(xs.begin(), xs.end(),
[x](A const& y) {return equivalent(x,y);}) != xs.end();
};
std::vector<A> push_back_if(std::vector<A> xs, A const& x) {
if ( !contains_equivalent(xs, x) ) {
xs.push_back(x);
}
return xs;
};
The function itself is just a call to std::accumulate:
std::vector<A> filter_all_but_one_for_each_set_of_equivalent_A(std::vector<A> as) {
return std::accumulate(as.begin(), as.end(), std::vector<A>{}, push_back_if);
}
I've modified AndyG's example code with my proposed function.
As defined above, std::accumulate calls push_back_if with a copy of the accumulator variable, and the return value is move-assigned to the accumulator again. This is very inefficient, but can be optimized by changing push_back_if to take a reference so that the vector is modified in-place. The initial value needs to be passed as a reference wrapper with std::ref to eliminate remaining copies.
std::vector<A>& push_back_if(std::vector<A>& xs, A const& x) {
if ( !contains_equivalent(xs, x) ) {
xs.push_back(x);
}
return xs;
};
std::vector<A> filter_all_but_one_for_each_set_of_equivalent_A(std::vector<A> const& as) {
std::vector<A> acc;
return std::accumulate(as.begin(), as.end(), std::ref(acc), push_back_if);
}
You can see in the example that the copy-constructor is almost completely eliminated.
First coming up with another loop version, in contrast to your own, it unifies in place, you might find it interesting:
std::vector<int> v({1, 7, 1, 8, 9, 8, 9, 1, 1, 7});
auto retained = v.begin();
for(auto i = v.begin(); i != v.end(); ++i)
{
bool isFirst = true;
for(auto j = v.begin(); j != retained; ++j)
{
if(*i == *j)
{
isFirst = false;
break;
}
}
if(isFirst)
{
*retained++ = *i;
}
}
v.erase(retained, v.end());
This was the base for a version using std::remove_if and std::find_if:
auto retained = v.begin();
auto c = [&v, &retained](int n)
{
if(std::find_if(v.begin(), retained, [n](int m) { return m == n; }) != retained)
return true;
// element remains, so we need to increase!!!
++retained;
return false;
};
v.erase(std::remove_if(v.begin(), v.end(), c), v.end());
You need the lambda in this case, as we need a unique-predicate, whereas equivalent (in my int example represented by operator==) is a binary one...
struct S {
int eq;
int value;
bool operator==(const S& other) const { return eq == other.eq; }
};
namespace std {
template <> struct hash<S>
{
size_t operator()(const S &s) const
{
return hash<int>()(s.eq);
}
};
}
array<S, 6> as{ { {1,0},{2,0},{3,0},{ 1,1 },{ 2,1 },{ 3,1 } } };
unordered_set<S> us(as.cbegin(), as.cend());
I have a template function inside which I want to generate a vector which is of an unknown type. I tried to make it auto, but compiler says it is not allowed.
The template function gets either iterators or pointers as seen in the test program inside the followed main function. How can the problem be fixed?
template<class Iter>
auto my_func(Iter beg, Iter end)
{
if (beg == end)
throw domain_error("empty vector");
auto size = distance(beg, end);
vector<auto> temp(size); // <--HERE COMPILER SAYS CANNOT BE AUTO TYPE
copy(beg, end, temp->begin);
.
.
return ....
}
int main()
{
int bips[] = {3, 7, 0, 60, 17}; // Passing pointers of array
auto g = my_func(bips, bips + sizeof(bips) / sizeof(*bips));
vector<int> v = {10, 5, 4, 14}; // Passing iterators of a vector
auto h = my_func(v.begin(), v.end());
return 0;
}
You cannot use a std::vector of auto. You might use std::iterator_traits instead:
std::vector<typename std::iterator_traits<Iter>::value_type> temp(size);
If you have a C++17-compatible compiler, you may profit from class template argument deduction.
So unless you have a specific reason to fill your vector with std::copy, you could write your code like this:
template<class Iter>
auto my_func(Iter beg, Iter end)
{
if (beg == end)
throw domain_error("empty vector");
vector temp(beg, end);
// do the remaining stuff
return ....
}
If this feature is not available on your compiler, then I'd vote for
vector<typename iterator_traits<Iter>::value_type> temp(beg, end);
like in Jonathan's answer
You might be looking for something like
std::vector<typename std::remove_reference<decltype(*beg)>::type> temp(beg, end);
Demo
The reason auto doesn't work is because it's not allowed in that context. You may not provide auto in place of a template argument. The correct course of action when you want the compiler to deduce a template argument automatically is to not provide an argument at all. However, in this case, there is no way for the compiler to deduce what that type should be. You must provide the type explicitly.
There are many ways of finding out what the correct type for your vector is. You can use std::iterator_traits to obtain information about an iterator, including the type of value it refers to. You would use typename std::iterator_traits<Iter>::value_type.
#include <algorithm>
#include <iterator>
#include <stdexcept>
#include <vector>
template<class Iter>
auto my_func(Iter beg, Iter end)
{
if (beg == end)
throw std::domain_error("empty vector");
auto size = std::distance(beg, end);
using t_value = typename std::iterator_traits<Iter>::value_type;
std::vector<t_value> temp(size);
std::copy(beg, end, temp.begin());
return temp;
}
int main()
{
int bips[] = { 3,7,0,60,17 };//Passing pointers of array
auto g = my_func(bips, bips + sizeof(bips) / sizeof(*bips));
std::vector<int> v = { 10,5,4,14 };//Passing iterators of a vector
auto h = my_func(v.begin(), v.end());
return 0;
}
I would like to point out that there is no reason to check for 0 size ranges. It would correctly return an empty vector.
You can also simplify the body of my_func quite a bit by taking advantage of the fact that std::vector has a constructor that accepts a pair of iterators and copies that range.
template<class Iter>
auto my_func(Iter beg, Iter end)
{
using t_value =typename std::iterator_traits<Iter>::value_type;
return std::vector<t_value>(beg, end);
}
You can extract a pointer/iterator's type information using iterator_traits. value_type is the specific trait that you are interested in, so you can do:
const vector<typename iterator_traits<Iter>::value_type> temp(beg, end);
Live Example
It is not true that the type is not known. The type of the vector you want to create is of the same kind of Iter.
Just get iter Underlying type either using decltype or using iterator type trait as follows:
decltype -> std::vector<typename remove_reference<decltype(*beg)>::type> temp(beg, end);
iterator type trait
as follows
using Type = std::iterator_traits<Iter>::value_type;
std::vector<Type>...
I would solve this slightly differently than your question seems to be asking for.
First, I find ranges to be a better fundamental type than taking two iterators. The two iterators are coupled, they should be one argument. A range is a simple struct of two iterators, with some utility methods:
template<class It>
struct range_t:
std::iterator_traits<It>
{
It b{}, e{};
It begin() const { return b; }
It end() const { return e; }
bool empty() const { return begin()==end(); }
auto size() const { return std::distance(begin(), end()); }
// etc
range_t()=default;
range_t(range_t const&)=default;
range_t(range_t &&)=default;
range_t& operator=(range_t const&)=default;
range_t& operator=(range_t &&)=default;
};
template<class It>
range_t<It> make_range( It s, It f ) { return {std::move(s), std::move(f)}; }
range_ts correctly couple the begin end iterator together.
Now
template<class Range>
auto my_func(Range&& range) {
// todo
}
template<class Iter>
auto my_func(Iter beg, Iter end)
{
return my_func(make_range(std::move(beg), std::move(end)));
}
is the first step. Or just eliminate the two-iterator version entirely, and expect the caller to package up their iterators for you.
template<class Range>
auto my_func(Range&& range) {
if (range.empty())
throw domain_error("empty vector");
// todo
}
Ok, now you want to do this:
auto size = range.size();
vector<auto> temp(size);//<--HERE COMPILER SAYS CANNOT BE AUTO TYPE
copy(range.begin(), range.end(), temp->begin);
but that is a common operation. So we write it for range:
template<class Range>
auto as_vector( Range const& r ) {
using value_type = typename Range::value_type;
std::vector<value_type> v( range.begin(), range.end() );
return v;
}
giving us:
template<class Range>
auto my_func(Range&& range) {
if (range.empty())
throw domain_error("empty vector");
auto v = as_vector(range);
// ...
return ...;
}
We have decomposed your problem into simple primitives that have meaning, and moved implementation complexity into those primitives. The "business logic" of your my_func no longer cares what steps you take to make a range into a vector.
This makes your my_func more readable, so long as you have some trust that as_vector(range) actually returns that range as a vector.