I have two vectors of classes that contain mainly strings, and I'm trying to keep track of how many times there was a match between two vectors. I kept an int counter in one of the two public classes (necessary for another function). However, std::find_if doesn't seem to allow me to modify nor assign this counter variable.
Following is the std::find_if search algorithm:
for (Vector1& v1 : vector1) {
auto res = find_if(vector2.begin(), vector2.end(),
[=](Vector2 v2) {
if (v2.code == v1.code) {
v1.counter++; // <-- where the error occurs
return true;
}
else
return false;
}
);
}
I can't seem to figure out why this happens; my speculation is that the third parameter for the find_if algorithm takes in a const value. But that shouldn't affect my vector1, right?
I used nested ranged for-loops instead, and it works perfectly. However, I'd like to try using this find_if algorithm instead...
You have a problem with capture/pass by value/reference.
It should be [&] or [&v1] - variables captured by value are non-mutable by default, and lambda's operator() is const. You could use the mutable keyword to fix the error, which makes operator() non-const, but you wouldn't see the changes made to v1 anyways.
Additionally, you should be passing by Vector2 const& v2, auto const& or auto && in sake of avoiding making a copy.
Together:
[&v1](Vector2 const& v2) { ... }
I'd like to try using this find_if algorithm instead...
But that's not what it's for. If you aren't going to use the returned iterator, don't do it. You should be getting a warning. Use loops for simple iteration.
Related
I am trying to do dfs over a graph defined as unordered_map<string,set<pair<string,int>>> g;
So here is my dfs code:
void dfs(string u){
for(auto v=g[u].begin();v!=g[u].end();v++){
if(!v->second){
cout<<v->first<<endl;
res.push_back(v->first);
v->second = 1;
dfs(v->first);
}
}
}
I am trying to change the value of v->second to 1, but I am getting an error of
cannot assign to return value because function 'operator->' returns a const value
v->second = 1;
So is there any other way to change the second value of the pair?
You cannot modify value of an element of std::set as it would break it's invariant. What you can do though is to remove existing element and insert modified one. But as you iterate over the set then such modification inside loop would be overcomplicated. So I suggest you remove all elements that satisfy your condition and then insert them all back modified.
Though it is not clear why you need int value of std::pair to be a part of the key. If that is a mistake, then just use std::unordered_map<string,std::map<string,int>> insted and then you can modify v->second without any problem (but that value would not be a part of the key anymore).
Note your loop is written quite ineffective way - you are invoking std::unordered_map::operator[] on every iteration. Instead you better get a reference to that element and use it:
void dfs(string u){
auto &gu = g[u];
for(auto v=gu.begin();v!=gu.end();v++){
...
The problem comes from begin() method of set. As stated in cpp ref:
Because both iterator and const_iterator are constant iterators (and may in fact be the same type), it is not possible to mutate the elements of the container through an iterator returned by any of these member functions.
This means the v is in fact const, so operator->() will be const version too. So you will not be able to change second.
BTW, a suggestion: use bool for visiting not int. it helps readability.
Full disclosure, this may be a hammer and nail situation trying to use STL algorithms when none are needed. I have seen a reappearing pattern in some C++14 code I am working with. We have a container that we iterate through, and if the current element matches some condition, then we copy one of the elements fields to another container.
The pattern is something like:
for (auto it = std::begin(foo); it!=std::end(foo); ++it){
auto x = it->Some_member;
// Note, the check usually uses the field would add to the new container.
if(f(x) && g(x)){
bar.emplace_back(x);
}
}
The idea is almost an accumulate where the function being applied does not always return a value. I can only think of a solutions that either
Require a function for accessing the member your want to accumulate and another function for checking the condition. i.e How to combine std::copy_if and std::transform?
Are worse then the thing I want to replace.
Is this even a good idea?
A quite general solution to your issue would be the following (working example):
#include <iostream>
#include <vector>
using namespace std;
template<typename It, typename MemberType, typename Cond, typename Do>
void process_filtered(It begin, It end, MemberType iterator_traits<It>::value_type::*ptr, Cond condition, Do process)
{
for(It it = begin; it != end; ++it)
{
if(condition((*it).*ptr))
{
process((*it).*ptr);
}
}
}
struct Data
{
int x;
int y;
};
int main()
{
// thanks to iterator_traits, vector could also be an array;
// kudos to #Yakk-AdamNevraumont
vector<Data> lines{{1,2},{4,3},{5,6}};
// filter even numbers from Data::x and output them
process_filtered(std::begin(lines), std::end(lines), &Data::x, [](int n){return n % 2 == 0;}, [](int n){cout << n;});
// output is 4, the only x value that is even
return 0;
}
It does not use STL, that is right, but you merely pass an iterator pair, the member to lookup and two lambdas/functions to it that will first filter and second use the filtered output, respectively.
I like your general solutions but here you do not need to have a lambda that extracts the corresponding attribute.
Clearly, the code can be refined to work with const_iterator but for a general idea, I think, it should be helpful. You could also extend it to have a member function that returns a member attribute instead of a direct member attribute pointer, if you'd like to use this method for encapsulated classes.
Sure. There are a bunch of approaches.
Find a library with transform_if, like boost.
Find a library with transform_range, which takes a transformation and range or container and returns a range with the value transformed. Compose this with copy_if.
Find a library with filter_range like the above. Now, use std::transform with your filtered range.
Find one with both, and compose filtering and transforming in the appropriate order. Now your problem is just copying (std::copy or whatever).
Write your own back-inserter wrapper that transforms while inserting. Use that with std::copy_if.
Write your own range adapters, like 2 3 and/or 4.
Write transform_if.
I need to change the "key" of a multiset:
multiset<IMidiMsgExt, IMidiMsgExtCompByNoteNumber> playingNotes;
such as that when I use the .find() function it search and return the first object (iterator) with that NoteNumber property value.
I said "first" because my multiset list could contains objects with the same "key". So I did:
struct IMidiMsgExtCompByNoteNumber {
bool operator()(const IMidiMsgExt& lhs, const IMidiMsgExt& rhs) {
return lhs.NoteNumber() < rhs.NoteNumber();
}
};
but when I try to do:
auto it = playingNotes.find(60);
the compiler says no instance of overloaded function "std::multiset<_Kty, _Pr, _Alloc>::find [with _Kty=IMidiMsgExt, _Pr=IMidiMsgExtCompByNoteNumber, _Alloc=std::allocator<IMidiMsgExt>]" matches the argument list
Am I misunderstanding the whole thing? What's wrong?
I do believe that you have some misunderstandings here:
Part of an associative container's type is it's key type and comparator. Because C++ is strongly typed the only way to change the comparator on a container is to create a new container, copying or moving all the elements into it
Creating a copy of all the elements in a container is a potentially expensive process
By creating a copy you are violating the Single Source of Truth best practice
multiset is used infrequently, I have used it once in my career, others have pointed out it's shortcomings and recommended that you use another container, write your own container, or in my case I'd suggests simply using vector and sorting it how you want when you have to
I'm going to catalog your comments to show how the answer I've already given you is correct:
We're going to assume that the multiset<IMidiMsgExt, IMidiMsgExtCompByNoteNumber> that you've selected is necessary and cannot be improved upon by using vector as suggested in 4, where:
struct IMidiMsgExtCompByNoteNumber {
bool operator()(const IMidiMsgExt& lhs, const IMidiMsgExt& rhs) {
return lhs.NoteNumber() < rhs.NoteNumber();
}
};
You cannot use multiset::find because that requires you tospecify the exact IMidiMsgExt you are searching for; so you'll need to use find_if(cbegin(playingNotes), cend(playingNotes), [value = int{60}](const auto& i){return i.mNote == value;}) to search for a specific property value. Which will be fine to use on to use directly on PlayingNotes without changing the sorting, because you say:
I want to delete the first note that has mNote of 60. No matter the mTime when deleting.
You'll need to capture the result of the [find_if], check if it is valid, and if so erase it as demonstrated in my answer, because you say:
The first element find will find for that, erase. [sic]
I would roll the code from my answer into a function because you say:
Ill recall find if I want another element, maybe with same value, to get deleted [sic]
Your final solution should be to write a function like this:
bool foo(const multiset<IMidiMsgExt, IMidiMsgExtCompByNoteNumber>& playingNotes, const int value) {
const auto it = find_if(cbegin(playingNotes), cend(playingNotes), [=](const auto& i){return i.mNote == value;});
const auto result = it != cend(playingNotes);
if(result) {
playingNotes.erase(it);
}
return result;
}
And you'd call it something like this: foo(playingNotes, 60) if you wish to know whether an element was removed you may test foo's return.
This question already has answers here:
Advantages of std::for_each over for loop
(22 answers)
Closed 7 years ago.
Let's consider a template function written in C++11 which iterates over a container.
Please exclude from consideration the range loop syntax because it is not yet supported by the compiler I'm working with.
template <typename Container>
void DoSomething(const Container& i_container)
{
// Option #1
for (auto it = std::begin(i_container); it != std::end(i_container); ++it)
{
// do something with *it
}
// Option #2
std::for_each(std::begin(i_container), std::end(i_container),
[] (typename Container::const_reference element)
{
// do something with element
});
}
What are pros/cons of for loop vs std::for_each in terms of:
a) performance? (I don't expect any difference)
b) readability and maintainability?
Here I see many disadvantages of for_each. It wouldn't accept a c-style array while the loop would. The declaration of the lambda formal parameter is so verbose, not possible to use auto there. It is not possible to break out of for_each.
In pre- C++11 days arguments against for were a need of specifying the type for the iterator (doesn't hold any more) and an easy possibility of mistyping the loop condition (I've never done such mistake in 10 years).
As a conclusion, my thoughts about for_each contradict the common opinion. What am I missing here?
I think there are some other differences not yet covered by the answers so far.
a for_each can accept any appropriate callable object, allowing one to 'recycle' the loop body for different for loops. For example (pseudo code)
for( range_1 ) { lengthy_loop_body } // many lines of code
for( range_2 ) { lengthy_loop_body } // the same many lines of code again
becomes
auto loop_body = some_lambda; // many lines of code here only
std::for_each( range_1 , loop_body ); // a single line of code
std::for_each( range_2 , loop_body ); // another single line of code
thus avoiding duplication and simplifying code maintenance. (Of course, in a funny mix of styles one could also use a similar approach with the for loop.)
another difference regards breaking out of the loop (with break or return in the for loop). As far as I know, in an for_each loop this can only be done by throwing an exception. For example
for( range )
{
some code;
if(condition_1) return x; // or break
more code;
if(condition_2) continue;
yet more code;
}
becomes
try {
std::for_each( range , [] (const_reference x)
{
some code;
if(condition_1) throw x;
more code;
if(condition_2) return;
yet more code;
} );
} catch(const_reference r) { return r; }
with the same effects regarding calling of destructors for objects with scope of the loop body and the function body (around the loop).
the main benefit of for_each is, IMHO, that one can overload it for certain container types, when plain iteration is not as efficient. For example, consider a container that holds a linked list of data blocks, each block containing a contiguous array of elements, similar to (omitting irrelevant code)
namespace my {
template<typename data_type, unsigned block_size>
struct Container
{
struct block
{
const block*NEXT;
data_type DATA[block_size];
block() : NEXT(0) {}
} *HEAD;
};
}
then an appropriate forward iterator for this type would require to check for the end of block at each increment and the comparison operator needs to compare both the block pointer and the index within each block (omitting irrelevant code):
namespace my {
template<typename data_type, unsigned block_size>
struct Container
{
struct iterator
{
const block*B;
unsigned I;
iterator() = default;
iterator&operator=(iterator const&) = default;
iterator(const block*b, unsigned i) : B(b), I(i) {}
iterator& operator++()
{
if(++I==block_size) { B=B->NEXT; I=0; } // one comparison and branch
return*this;
}
bool operator==(const iterator&i) const
{ return B==i.B && I==i.I; } // one or two comparisons
bool operator!=(const iterator&i) const
{ return B!=i.B || I!=i.I; } // one or two comparisons
const data_type& operator*() const
{ return B->DATA[I]; }
};
iterator begin() const
{ return iterator(HEAD,0); }
iterator end() const
{ return iterator(0,0); }
};
}
this type of iterator works correctly with for and for_each, for example
my::Container<int,5> C;
for(auto i=C.begin();
i!=C.end(); // one or two comparisons here
++i) // one comparison here and a branch
f(*i);
but requires two to three comparisons per iteration as well as a branch. A more efficient way is to overload the for_each() function to loop on the block pointer and index separately:
namespace my {
template<typename data_type, int block_size, typename FuncOfDataType>
FuncOfDataType&&
for_each(typename my::Container<data_type,block_size>::iterator i,
typename my::Container<data_type,block_size>::iterator const&e,
FuncOfDataType f)
{
for(; i.B != e.B; i.B++,i.I=0)
for(; i.I != block_size; i.I++)
f(*i);
for(; i.I != e.I; i.I++)
f(*i);
return std::move(f);
}
}
using my::for_each; // ensures that the appropriate
using std::for_each; // version of for_each() is used
which requires only one comparison for most iterations and has no branches (note that branches can have a nasty impact on performance). Note that we don't need to define this in namespace std (which might be illegal), but can ensure that the correct version is used by appropriate using directives. This is equivalent to using std::swap; when specialising swap() for certain user-defined types.
Regarding perfomance, your for loop calls std::end repeatedly, while std::for_each will not. This might or might not result in a performance difference depending on the container used.
The std::for_each version will visit each element exactly once. Somebody reading the code can know that as soon as they see std::for_each, as there's nothing that can be done in the lambda to mess with the iterator. In the traditional for loop, you have to study the body of the loop for unusual control flow (continue, break, return) and dinking with the iterator (e.g., in this case, skip the next element with ++it).
You can trivially change the algorithm in the lambda solution. For example, you could make an algorithm that visits every nth element. In many cases, you didn't really want a for loop anyway, but a different algorithm like copy_if. Using an algorithm+lambda, is often more amenable to change and is a bit more concise.
On the flip side, programmers are much more used to traditional for loops, so they may find algorithm+lambda to be harder to read.
First, I cannot see much difference between these two, because for_each is implemented using for loop. But note that for_each is a function which has a return value.
Second, I will use range loop syntax once available in this case since this day would come soon anyway.
Indeed; in the case of using a Lambda expression, you have to declare the parameter type and name, so nothing is won.
But it will be awesome as soon as you want to call one (named) function or function-object with this. (Remember that you can combine function-like things via std::bind.)
The books from Scott Meyers (I believe it was Effective STL) describe such programming styles very good and clear.
I was thinking about using remove_if on a vector of strings as follows in the pseudo code below:
for(some strings in a given set) {
remove_if(myvec.begin(), myvec.end(), string_matches_current_string);
}
Now, I am aware I can define the predicate and make this work easily. But I was wondering if there is a standard template function that I could use in place of the predicate above to make this work. I was looking around and couldn't find one. Appreciate any ideas with examples. Thanks!
Why using std::remove_if if you already know the value you want to remove? Use std::remove, which removes the items in the provided range, that match the given value:
std::vector<std::string>::iterator new_end = my_vec.end();
for(const auto ¤t_set_string : some_set)
new_end = std::remove(myvec.begin(), new_end, current_set_string);
my_vec.erase(new_end, my_vec.end()); // effectively remove them from the vector.
Note that I used the range-based for loop just to make this shorter, but you should use a regular loop if you can't use C++11.
I'm pretty sure there isn't a standard function but you can easily write the whole expression using a C++11 lambda:
std::remove_if(myvec.begin(), myvec.end(),
[&compare_me](std::string const& cmp) -> bool
{
return compare_me == cmp;
});
With compare_me being the "current" string set by the outer loop.
Keep in mind that remove_if returns an iterator to one past the last valid element so in order to get the correct myvec, you have to erase the elements between the iterator returned by remove_if and myvec.end().
For a non-C++11 implementation you'd have to turn the lambda into a function or functor. If you turn it into a functor you can pass the functor directly, if you turn it into a function you'll have to use something like boost::bind to provide the necessary glue.