I'm having a problem passing predicate using lambda, I'm trying to move element that matches the predicate to the beginning of a second container, but it didn't seem to work, so what's wrong please?
#include <iostream>
#include <vector>
#include <list>
#include <iterator>
#include <utility>
#include <algorithm>
using namespace std;
template <typename iterator, typename Container, typename T>
void move_if(iterator b, iterator e, Container o, T pred)
{
if(pred)
{
o.insert(o.begin(),pred);
}
}
int main()
{
vector<int>v{1,2,3,4,5,6,7,8,9,10};
vector<int>v2;
for (auto i=v.begin(); i !=v.end(); ++i)
save_if(v.begin(), v.end(), v2, []( vector<int>::iterator i){return (*i>5);});
return 0;
}
Try this...
int main()
{
std::vector<int> v{1,2,3,4,5,6,7,8,9,10};
std::vector<int> v2;
std::vector<int>::const_iterator
it = std::remove_copy_if(v.begin(), v.end(),
std::back_inserter(v2),
[](int const& i){return i <= 5;});
v.erase(it, v.end);
return 0;
}
You can read more about remove_copy_if on cppreference.com; it removes elements from the input range and copies them to the output unless the predicate returns true.
Note that this is an STL remove, so you need to call erase afterwards to shrink the input. The semantics of this solution are slightly different to the code you posted, but more similar to your description of what you wanted.
Check this out, I did some modifications on your code:
template <typename iterator, typename Container, typename T>
void move_if(iterator a, iterator b, Container &o, T pred)
{
for (auto i = a; i != b; i++)
{
if (pred(*i))
o.insert(o.begin(), *i);
}
}
int main()
{
vector<int>v{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
vector<int>v2;
move_if(v.begin(), v.end(), v2, [](int i) { return !(i > 5); });
}
Note: As the comments, It's recommended to rename move_if to copy_if, if the functionality is as above code, otherwise you should really move items.
There is no overload of std::vector::insert that takes a predicate as second argument, so this line is wrong:
o.insert(o.begin(),pred);
Furthermore, the predicate needs to be called with an argument,
pred(someArg);
which in your case would be an std::vector<int>::iterator. Also, save_if is not the same as move_if. But more importantly, it isn't clear at all what you are trying to achieve.
In C++11, stateless lambdas like [](){return true} that do not capture anything can be implicitly converted to function pointers. When you do if(pred) you are converting your stateless lambda into a function pointer, checking if that pointer is non-null (it is non-null). This is not what you want to do.
Here is an implementation that moves things between b and e that pred(x) says should be moved:
template <typename iterator, typename Container, typename T>
void move_if(iterator b, iterator e, Container o, T pred)
{
for( auto i = b; i != e;++i) {
if(pred) {
o.insert(o.end(),std::move(*i));
}
}
}
Note that I inserted at o.end(), because the Container you want is probably vector, and inserting at the end() of vector is much faster.
In reality, you probably want to take an output iterator (and by default, use std::back_inserter from a Container) and output your data to that. Similarly, remove_move_if would be a better way to remove, shuffling the elements down the b-e range, and returning an iterator.
Finally, ranged-based algorithms are worth writing. Instead of taking a begin/end iterator pair, take a single object upon which begin(c) and end(c) have been overriden to return begin/end. If you are working on a sub-range, you can pass in a begin/end range of iterators struct with begin/end suitably overridden.
Related
I want my function to be able to take array.begin() and array.end() as arguments. As far as I understand, the begin/end functions return a pointer to the first/last element of the array. Then why does the following code not work? How should I write the code instead?
#include <iostream>
#include <array>
template<class T> void foo(const T* begin, const T* end) {
...
}
int main() {
std::array<int, 5> arr = { 1, 2, 3, 4, 5 };
foo(arr.begin(), arr.end()); // <-- error!
return 0;
}
As far as I understand, the begin/end functions return a pointer to the first/last element of the array
No. begin and end return iterators. The standard library works with iterators. Iterators are a generalization of pointers.
Iterators behave like pointers and you use them like pointers (e.g. *it to access the element), with some caveats: not all iterators have all the operations a pointer does. A pointer satisfies the random access iterator concept, so on some implementations the iterator of std::array could be just an alias for the pointer type, but you can't rely on that. E.g. on the same compiler it can be a pointer for the release build, but a full class for the debug build.
The idiomatic way is to write:
template<class It>
void foo(It begin, It end) {
for (auto it = begin; it != end; ++it) {
const auto& elem = *it;
// ..
}
}
Since C++20 we should transition from iterator pairs to ranges:
void foo(std::ranges::range const auto& r) {
for (const auto& elem : r) {
// ...
}
}
As far as I understand, the begin/end functions return a pointer
Your understanding is (generally) wrong. begin and end functions return std::array::iterator, as per documentation. That type is not necessarily a pointer.
to the first/last element of the array
end isn't an iterator to the last element, but one past the last element.
how to solve my problem the right way?
Accept arguments of type std::array::iterator.
That said, given that foo is a template, there seems to be little reason to not allow the argument to be of any iterator type:
template<class It>
void
foo(It begin, It end)
Alternatively, you could accept a range:
template<class Range>
void
foo(const Range& range)
// usage
foo(arr);
I have a container that has no push_back() method. But this container has an iterator on begin()and end().
I would like to use std::transform() to output into that container. But std::back_inserter needs to call push_back() on the output container.
Is it possible to use std::transform() to output into a container that just supports direct assignment? Like:
for (auto item : containerNoPushBack)
{
item = calculateValue();
}
Or indexed assignment like:
for (size_t i = 0; i < containerNoPushBack.size(); ++i)
{
item[i] = calculateValue();
}
If you can do item[i] (i.e. your container has enough elements in it), then you can simply use your_container.begin() in std::transform, no need for std::back_inserter. std::back_inserter is just a nice way to avoid explicitly resizing containers before applying it.
The following snippets will both fill my_output with contents of my_input:
std::vector<int> my_output;
std::transform(my_input.begin(), my_input.end(), std::back_inserter(my_output), [](const auto& arg){return arg;});
std::vector<int> my_output;
my_output.resize(my_input.size());
std::transform(my_input.begin(), my_input.end(), my_output.begin(), [](const auto& arg){return arg;});
Just use a plain std::transform with begin and end iterators. No need for std::back_inserter or even an std::inserter.
For example, std::array does not support the push_back method, but std::transform works on it:
#include <iostream>
#include <array>
#include <algorithm>
#include <vector>
int main() {
std::vector<int> data = {1, 2, 3, 4, 5};
std::array<int, 5> arr{};
// note the lack of std::back_inserter just below
std::transform(data.cbegin(), data.cend(), arr.begin(),
[](auto i) {
return i * i;
});
for (const auto i : arr) {
std::cout << i << ' '; // prints 1, 4, 9, 16, 25
}
}
std::transform does use "direct assignment". only if you need to push the elements you would use a back_inserter, if the target has already elements you dont need that.
Consider the possible implementation (taken from cppreference):
template<class InputIt, class OutputIt, class UnaryOperation>
OutputIt transform(InputIt first1, InputIt last1, OutputIt d_first,
UnaryOperation unary_op)
{
while (first1 != last1) {
*d_first++ = unary_op(*first1++);
}
return d_first;
}
std::transform itself does not push anything. Exactly for that reason you need a back_inserter. However, instead you can resize the target to have enough space for the transformed elements.
Say I have a vector with various entries, which I want to insert into another vector, while leaving out entries that satisfy a condition.
For example, I want to insert a vector while leaving out all three's.
{1, 3, 2, 3, 4, 5, 3} -> { /* previous content, */ 1, 2, 4, 5}
What I came up with so far uses std::partition, which does not preserve the relative order and rearranges the source vector.
std::vector<int> source({1, 3, 2, 3, 4, 5, 3});
std::vector<int> target;
auto partition = std::partition(std::begin(source),
std::end(source), [](const auto& a) { return a == 3; });
target.insert(std::begin(target), partition, std::end(source));
What I am looking for is more of an iterator that checks a condition and moves on if the condition is not satisfied. Something like this:
target.insert(std::begin(target),
conditional_begin(source, [](const auto& a) { return a != 3; }),
conditional_end(source));
I suppose a conditional_end function would be necessary, since std::end would return a different iterator type than conditional_begin.
Maybe I have overlooked something, so my questions are:
Does the standard library provide something similar?
Is there a different easy way to achieve my goal?
Is there an easy way to implement the conditional iterator functionality?
Is there a different easy way to achieve my goal?
Yes, the standard already has this functionality built in. The function you are looking for is std::copy_if.
std::vector<int> source({1, 3, 2, 3, 4, 5, 3});
std::vector<int> target;
std::copy_if(source.begin(),
source.end(),
std::back_inserter(target), [](auto val){ return val != 3; });
Here, std::back_inserter(target), will call push_back on target for each element that the predicate returns true.
Yes, you can create a custom iterator that does what you want but it is currently a little tedious to create custom iterators using standard C++. It would look something like this:
template <typename Itr, typename F>
struct ConditionalIterator {
Itr itr;
Itr end;
F condition;
using value_type = typename Itr::value_type;
using difference_type = typename Itr::difference_type;
using pointer = typename Itr::pointer;
using reference = typename Itr::reference;
using iterator_category = std::forward_iterator_tag;
ConditionalIterator() = default;
ConditionalIterator(Itr itr, Itr end, F condition): itr(itr), end(end), condition(condition) {}
bool operator!=(const ConditionalIterator &other) const { return other.itr != itr; }
reference operator*() const { return *itr; }
pointer operator->() const { return &(*itr); }
ConditionalIterator& operator++() {
for (; ++itr != end;) {
if (condition(*itr))
break;
}
return *this;
}
ConditionalIterator operator++(int) {
ConditionalIterator ret(*this);
operator++();
return ret;
}
};
You can then create something like the conditional_begin and conditional_end helper functions you asked for. The only issue is that std::vector::insert expects the two iterators to have the same type. If we use a lambda for our condition then this will be part of the type of our conditional iterator. So we need to pass the lambda to both helper functions so that they return iterators with matching types:
template <typename C, typename F>
auto conditional_begin(const C &source, F f) {
return ConditionalIterator<typename C::const_iterator, F>(source.begin(),
source.end(), f);
}
template <typename C, typename F>
auto conditional_end(const C &source, F f) {
return ConditionalIterator<typename C::const_iterator, F>(source.end(),
source.end(), f);
}
Which you could call with a lambda like this:
auto condition = [](const auto &a) { return a != 3; };
target.insert(std::begin(target),
conditional_begin(source, std::ref(condition)),
conditional_end(source, std::ref(condition)));
Live demo.
My crude tests show, in this case, this ends up being significantly faster than simply using copy_if and back_inserter because std::vector::insert first works out how much memory to allocate before inserting. Just using back_inserter will cause multiple memory allocations. The difference in performance will depend on how expensive the condition is to evaluate. You can get the same speedup by using count_if to reserve enough space before using copy_if:
auto count = static_cast<size_t>(std::count_if(source.begin(),
source.end(), condition));
target.reserve(target.size() + count);
std::copy_if(source.begin(),
source.end(),
std::back_inserter(target), condition);
Live demo.
As ranges will be standardized soon, this is an alternative using range-v3, the reference library for the proprosal:
#include <range/v3/view/concat.hpp>
#include <range/v3/view/filter.hpp>
using namespace ranges;
const std::vector<int> source{1, 3, 2, 3, 4, 5, 3};
const std::vector<int> target = view::concat(source,
source | view::filter([](auto i){ return i != 3; }));
Consider a function that accepts one or more parameters (e.g. 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.
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.
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.
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
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 });
}
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.