std::find / find_if versus for loop

M

ma740988

Trying to get more acclimated with the use of function objects. As
part of my test, consider:

# include <vector>
# include <iostream>
# include <algorithm>
#include <stdexcept>
#include <bitset>

using std::vector;
using std::cout;
using std::endl;
using std::eek:stream;

using std::bitset;

class bar {
int iden;
public:
bar(int id) : iden(id) {}
int get_id() const { return iden; }
};
class my_foo {
typedef std::vector <bar> BAR_VEC;
BAR_VEC bv;

public:
my_foo()
{
bv.push_back(bar(0));
bv.push_back(bar(1));
bv.push_back(bar(3));
}
void find ( int id_to_find )
{
BAR_VEC::const_iterator end = bv.end();
for ( BAR_VEC::iterator it = bv.begin(); it != end; ++it )
{
if ( id_to_find == it->get_id() )
{
std::cout << " found " << id_to_find << std::endl;
}
}
// BAR_VEC seg_iter = std::find(
// bv.begin(), bv.end(),
// XXX ); // XXX now the fancy functor or predicate business - i
suppose

}
};


int main()
{
my_foo m;
m.find( 0 ) ;
m.find( 1 ) ;
m.find( 9 ) ;
}

Now within the find function. I'd like to use std::find or
std::find_if to achieve the same result as the for loop but I'm
getting wrapped around the axle trying to 'plug' a parameter/functor
into the third argument of find.
Again this is pure experiment. Perusing a text here and I'm just
trying to create examples as I go along, in an attempt to embrace
different views of iterating across containers/ (where necessary ) call
member functions etc. beyond the convential for loop.
 
C

cbsmith

There are different ways to go with this, but I'd go with something
like:

struct IsId : public std::binary_function<int, bar, bool> {
bool operator()(int anId, const bar& aBar) const {
return anId == aBar.get_id();
}
};

BAR_VEC::const_iterator seg_iter = std::find_if(bv.begin(), bv.end(),
std::bind1st(IsId(), id_to_find));
if (seg_iter != bv.end()) {
std::cout << " found " << id_to_find << std::endl;
}

IMHO it's prettier if you use std::for_each, but this approach has the
more efficient fast failure.

--Chris
 
M

ma740988

BAR_VEC::const_iterator seg_iter = std::find_if(bv.begin(), bv.end(),
std::bind1st(IsId(), id_to_find));
if (seg_iter != bv.end()) {
std::cout << " found " << id_to_find << std::endl;
}

IMHO it's prettier if you use std::for_each, but this approach has the
more efficient fast failure.

I'm reading that function objects with state( whatever that means-
haven't figured out 'state' yet ) leads to unpredictable results when
passed to for_each.

I haven't figured out how to check against bv.end yet but heres what
I've got thus far:

std::for_each(bv.begin(), bv.end(), std::bind1st(IsId(), id_to_find));
 
D

Daniel T.

"ma740988 said:
Trying to get more acclimated with the use of function objects. As
part of my test, consider:

# include <vector>
# include <iostream>
# include <algorithm>
#include <stdexcept>
#include <bitset>

using std::vector;
using std::cout;
using std::endl;
using std::eek:stream;

using std::bitset;

class bar {
int iden;
public:
bar(int id) : iden(id) {}
int get_id() const { return iden; }
};
class my_foo {
typedef std::vector <bar> BAR_VEC;
BAR_VEC bv;

public:
my_foo()
{
bv.push_back(bar(0));
bv.push_back(bar(1));
bv.push_back(bar(3));
}
void find ( int id_to_find )
{
BAR_VEC::const_iterator end = bv.end();
for ( BAR_VEC::iterator it = bv.begin(); it != end; ++it )
{
if ( id_to_find == it->get_id() )
{
std::cout << " found " << id_to_find << std::endl;
}
}
// BAR_VEC seg_iter = std::find(
// bv.begin(), bv.end(),
// XXX ); // XXX now the fancy functor or predicate business - i
suppose

}
};


int main()
{
my_foo m;
m.find( 0 ) ;
m.find( 1 ) ;
m.find( 9 ) ;
}

Now within the find function. I'd like to use std::find or
std::find_if to achieve the same result as the for loop but I'm
getting wrapped around the axle trying to 'plug' a parameter/functor
into the third argument of find.
Again this is pure experiment. Perusing a text here and I'm just
trying to create examples as I go along, in an attempt to embrace
different views of iterating across containers/ (where necessary ) call
member functions etc. beyond the convential for loop.

First I'll give the answer, then I'll explain how I got there.

The answer is:

void my_foo::find(int id_to_find) {
if (find_if(bv.begin(), bv.end(), compose1(bind2nd(equal_to<int>(),
id_to_find ), mem_fun_ref( &bar::get_id ) ) ) != bv.end() )
cout << "found " << id_to_find << "\n";
}

Now how did I come up with that answer?

First just extract the entire guts into a struct:

struct foo : public unary_function<bar, bool> {
int id_to_find;
foo(int i): id_to_find(i) { }
bool operator()(const bar& bar_) const {
return bar_.get_id() == id_to_find;
}
};

void my_foo::find(int id_to_find)
{
if (find_if(bv.begin(), bv.end(), foo(id_to_find)) != bv.end())
cout << "found " << id_to_find << "\n";
}

(Note: I really have to do the above before I can come up with an
answer, the rest I can usually get without having to actually compile
each step. In other words, with practice, you should be able to go
straight from the above to the correct answer.)

Now we tease out some of the guts into standard functors. Our goal here
is to end up with a functor that accepts a 'bar' object because that is
what is in the container.

The last thing that happens in the 'foo' struct's op() is the
comparison, so:

struct foo : public unary_function<bar, bool> {
int id_to_find;
foo(int i): id_to_find(i) { }
bool operator()(const bar& bar_) const {
equal_to<int> a = equal_to<int>();
return a(bar_.get_id(), id_to_find);
}
};

Now we have a functor 'a' that accepts two ints, one of which comes from
our bar object. Let's remove the second int from it by binding that
second arg:

struct foo : public unary_function<bar, bool> {
int id_to_find;
foo(int i): id_to_find(i) { }
bool operator()(const bar& bar_) const {
binder2nd<equal_to<int> > b =
bind2nd(equal_to<int>(), id_to_find);
return b(bar_.get_id());
}
};

Now we have a functor 'b' that accepts the results of bar::get_id. Let's
bind that up into a functor as well:

struct foo : public unary_function<bar, bool> {
int id_to_find;
foo(int i): id_to_find(i) { }
bool operator()(const bar& bar_) const {
binder2nd<equal_to<int> > b =
bind2nd(equal_to<int>(), id_to_find);
const_mem_fun_ref_t<int, bar> c =
mem_fun_ref(&bar::get_id);
return b(c(bar_));
}
};

Now we have our functor 'c' that accepts a bar object, but it needs to
pass its results to another functor, and there is nothing in the std
namespace that can help us. Fortunately, there is an STL functor that
can do the job. <http://www.sgi.com/tech/stl/unary_compose.html>

struct foo : public unary_function<bar, bool> {
int id_to_find;
foo(int i): id_to_find(i) { }
bool operator()(const bar& bar_) const {
stl::unary_compose<binder2nd<equal_to<int> >,
const_mem_fun_ref_t<int, bar> > d =
compose1(bind2nd(equal_to<int>(), id_to_find),
mem_fun_ref(&bar::get_id));
return d(bar_);
}
};

There we go. The functor 'd' accepts a bar object and returns the
results we wanted. Just replace the 'foo' functor in the 'my_foo::find'
function with everything to the left of the 'd =' above.

Here is that 'unary_compose' and 'compose1' functor and function...

template <typename Op1, typename Op2>
class unary_compose:
public std::unary_function<typename Op2::argument_type,
typename Op1::result_type>
{
Op1 fn1;
Op2 fn2;
public:
unary_compose(const Op1& x, const Op2& y): fn1(x), fn2(y) {}
typename Op1::result_type operator()(const typename
Op2::argument_type& x) const {
return fn1(fn2(x));
}
};

template <typename Op1, typename Op2>
inline unary_compose<Op1, Op2> compose1(const Op1& fn1, const Op2& fn2) {
return unary_compose<Op1, Op2>(fn1, fn2);
}
 
N

Nitin Motgi

There are different ways to go with this, but I'd go with something
like:

struct IsId : public std::binary_function<int, bar, bool> {
bool operator()(int anId, const bar& aBar) const {
return anId == aBar.get_id();
}
};

Can predicate model use the binary_function or should it be only
unary_function.

Thank you,
Nitin Motgi
 
R

Roland Pibinger

The answer is:

void my_foo::find(int id_to_find) {
if (find_if(bv.begin(), bv.end(), compose1(bind2nd(equal_to<int>(),
id_to_find ), mem_fun_ref( &bar::get_id ) ) ) != bv.end() )
cout << "found " << id_to_find << "\n";
}

Now how did I come up with that answer?

[dozens of lines with geek-style code snipped]

You mean, you would really replace a simple for-loop with that? LOL!

Regards,
Roland Pibinger
 
D

Daniel T.

The answer is:

void my_foo::find(int id_to_find) {
if (find_if(bv.begin(), bv.end(), compose1(bind2nd(equal_to<int>(),
id_to_find ), mem_fun_ref( &bar::get_id ) ) ) != bv.end() )
cout << "found " << id_to_find << "\n";
}

Now how did I come up with that answer?

[dozens of lines with geek-style code snipped]

You mean, you would really replace a simple for-loop with that? LOL!

Not necessarily, as the OP said, it is merely an exorcise.
 
M

ma740988

Daniel said:
First I'll give the answer, then I'll explain how I got there.

The answer is:

void my_foo::find(int id_to_find) {
if (find_if(bv.begin(), bv.end(), compose1(bind2nd(equal_to<int>(),
id_to_find ), mem_fun_ref( &bar::get_id ) ) ) != bv.end() )
cout << "found " << id_to_find << "\n";
}
I read this and I get depressed. I've got such a loooong (sic) ways to
go, before I could put that together :) I'm sitting here trying to
create scenarios for usage on some of these algorithms. Get this:
make_heap, push_heap, pop_heap, sort_heap. I'm thinking for what
purpose? Oh, well I'll get back to those in a minute - or just skip
those all together.

In any event, thanks for the tip.
 
D

Daniel T.

"ma740988 said:
Daniel T. wrote:
I read this and I get depressed. I've got such a loooong (sic) ways to
go, before I could put that together :) I'm sitting here trying to
create scenarios for usage on some of these algorithms. Get this:
make_heap, push_heap, pop_heap, sort_heap. I'm thinking for what
purpose? Oh, well I'll get back to those in a minute - or just skip
those all together.

In any event, thanks for the tip.

From Bjarne Stroustrup, "Is this readable?... The notation is logical,
but it does take some getting used to."

As others have pointed out, what exactly is wrong with:

void my_foo::find( int id_to_find ) {
vector<bar>::const_iterator end = bv.end();
for ( vector<bar>::const_iterator it = bv.begin();
it != end && it->get_id() != id_to_find; ++it ) { }
if ( it != end )
cout << "found " << id_to_find << "\n";
}

After all, that is what my grand find_if/compose1/&c. "functor
construct" is doing isn't it?

Well, they are both as efficient.

Is the for loop easer to read? The functor construct uses words like
'find_if' and 'equal_to'. Curiously, the for loop needs to do a != to
find an element that is equal to the value we are seeking...

Is the for loop easer to write? Initially, the for loop does seem easer,
until you look at the condition in it. You better have a strong grasp of
operator precedence and will you catch the != bit to find the element
that is equal? Sure, for someone who came from a C background and
doesn't know the functor library that well, the for loop requires less
work to create. However, the whole point of moving from C to C++ (for me
at least) was to move to a higher level of abstraction (but still have
access to the low-level when needed...)

Of course the best solution is probably somewhere in-between, maybe:

struct bar_id_eq_to {
int id_to_find;
bar_id_eq_to(int i): id_to_find(i) { }
bool operator()( const bar& b ) const {
return b.get_id() == id_to_find;
}
};

void my_foo::find( int id_to_find ) {
if (find_if(bv.begin(), bv.end(), bar_id_eq_to(id_to_find)) !=
bv.end())
cout << "found " << id_to_find << "\n";
}

And finally another quote from Mr. Stroustrup, "A key benefit of the
standard library algorithms is that they save the programmer from
writing explicit loops. Loops can be tedious and error-prone."

You learned that already, your original loop doesn't stop once it finds
an element but continues all the way to the end of the loop. But maybe
you wanted that behavior from the beginning? Then I would turn to
something like:

int total = count_if( bv.begin(), bv.end(),
compose1(bind2nd(equal_to<int>(), id_to_find),
mem_fun_ref(&bar::get_id)));
for ( int i = 0; i < total; ++i )
cout << "found " << id_to_find << "\n";


or

struct output_if_id_is {
int id;
output_if_id_is( int i ): id(i) { }
void operator()( const bar& b ) const {
if ( b.get_id() == id )
cout << " found " << id << "\n";
}
};

and

for_each( bv.begin(), bv.end(), output_if_id_is( id_to_find ) );
 
D

Daniel T.

BAR_VEC::const_iterator seg_iter = std::find_if(bv.begin(), bv.end(),
std::bind1st(IsId(), id_to_find));
if (seg_iter != bv.end()) {
std::cout << " found " << id_to_find << std::endl;
}

IMHO it's prettier if you use std::for_each, but this approach has the
more efficient fast failure.

I'm reading that function objects with state( whatever that means-
haven't figured out 'state' yet ) leads to unpredictable results when
passed to for_each.[/QUOTE]

Where are you reading that?
 
M

ma740988

Agreed, it exorcises the KISS spirit from the program!
;-)
No disrespect, but your comments amount to "no value added" and it that
case, there's a saying that some things are better left unsaid.
Again, all I'm trying to do is learn how to use some of these
algorithms. The frinkin C++ standard is filled with a phletora of
algorithms. So I asked myself. Will I ever need 'half' of that mess?
The answer is perhaps no but that's irrelevant. I felt a more
proactive approach would involve experimenting with some 'test' cases.
erase, find and find_if seem like some of the more common ones. After
all some containers have their own versions of erase and find. In any
event, for experiment, I started to pick on them.

I know/am well aware of the simple for loop (I _did_ that) and the need
to KISS. I also know that a _fairly competent programmer_ (like most
folks on this board- believe me I've scanned the group and seem some
code) would perhaps use some combination of algorithms. For instance,
my all time favorite. erase an element from a vector. You're almost
always going to get some combination of std::erase and std::find. All
I'm trying to do is learn those types of things and keep them in my
pocket. It's as if I'm given vector's and deque's but I'm still
mucking around with C style arrays. Yes, C style arrays have worked
_forever_ and will still work but I'm not convinced that's the right
attitude to have.

An oh!! Didn't one of these authors ( I think it was Bjarne ) mention
that for loops can be error prone. I question what's so error prone
about the for loop versus the find but maybe I'll find out.

Perhaps, I'm taking your comments a tad bit serious, but in the
meantime, find somebody else's post to answer.
 
M

ma740988

From Bjarne Stroustrup, "Is this readable?... The notation is logical,
but it does take some getting used to."
I'll need to go chase down where he said this but point taken
Sure, for someone who came from a C background and
doesn't know the functor library that well, the for loop requires less
work to create. However, the whole point of moving from C to C++ (for me
at least) was to move to a higher level of abstraction (but still have
access to the low-level when needed...)
Well said.
And finally another quote from Mr. Stroustrup, "A key benefit of the
standard library algorithms is that they save the programmer from
writing explicit loops. Loops can be tedious and error-prone."
I'll buy it but to me, when I first encounted this statement. tedious,
Ok!!
error-prone. I could easily ruin - say a predicate.

You learned that already, your original loop doesn't stop once it finds
an element but continues all the way to the end of the loop. But maybe
you wanted that behavior from the beginning?
No I didn't. I suspect that's an error-prone scenario
 
M

ma740988

You learned that already, your original loop doesn't stop once it finds
an element but continues all the way to the end of the loop. But maybe
you wanted that behavior from the beginning? Then I would turn to
something like:

Actually, here's my test scenario and what I'm trying to do:


/*
Given a vector of my_struct1 and a vector of my_struct2.
I'll compare the vector of my_struct1 with my_struct2 for equality:

To do this:
Consider
my_struct1 (in main below) - told me I should have
a segment_id - 5, data_size - 0x100, value - 0xA5.

Well, I'll walk through my_struct2.
In my_struct2, there's a segment_id - 5, data_size - 0x100.

The question then becomes do I have a value of 0xA5 in
my_struct2. To determine this.
I search the address - ptr_addr in my_struct2 to detemine
if the value is 0xA5 for the length of 0x100
*/

# include <vector>
# include <iostream>
# include <algorithm>
#include <fstream>

using std::cout;
using std::endl;
using std::vector;
using std::eek:stream;


typedef unsigned int uint_type;
struct my_struct1 {
uint_type segment_id;
uint_type data_size;
uint_type value;
};

typedef unsigned char* ADDR_TYPE;
struct my_struct2 {
uint_type segment_id;
uint_type data_size;
ADDR_TYPE ptr_addr;
};

typedef std::vector<my_struct1> MS1;
typedef std::vector<my_struct2> MS2;

int main()
{
my_struct1 ms11, ms12;
ms11.segment_id = 5;
ms11.data_size = 0x100;
ms11.value = 0xA5;

MS1 m1;
m1.push_back(ms11);

ms12.segment_id = 2;
ms12.data_size = 0x400;
ms12.value = 0xB5;
m1.push_back(ms12);

int *ptr_addr1 = new int[0x100];
std::fill ( ptr_addr1, ptr_addr1 + 0x100, 0xA5 );

my_struct2 ms21, ms22;
ms21.segment_id = 5;
ms21.data_size = 0x100;
ms21.ptr_addr = (ADDR_TYPE)ptr_addr1;

MS2 m2;
m2.push_back(ms21);

int *ptr_addr2 = new int[0x400];
std::fill ( ptr_addr2, ptr_addr2 + 0x400, 0xB5 );

ms22.segment_id = 2;
ms22.data_size = 0x400;
ms22.ptr_addr = (ADDR_TYPE)ptr_addr2;

m2.push_back(ms22);
delete [] ptr_addr1;
delete [] ptr_addr2;
}
 
D

Daniel T.

From Bjarne Stroustrup, "Is this readable?... The notation is logical,
but it does take some getting used to."

I'll need to go chase down where he said this but point taken[/QUOTE]

Section 18.4.4.1 Binders (page 520 in my edition.)
I'll buy it but to me, when I first encounted this statement. tedious,

Ok!!
error-prone. I could easily ruin - say a predicate.

Not likely. :) *If* you get it to compile, it will probably be right.
Unlike the loop which will probably compile the first time, but is more
likely to produce the wrong answer, (or worse the right answer but it
has some other subtle bug that doesn't show up for a while, like your
original loop.)
 
D

Daniel T.

"ma740988 said:
Again, all I'm trying to do is learn how to use some of these
algorithms. The frinkin C++ standard is filled with a phletora of
algorithms. So I asked myself. Will I ever need 'half' of that mess?
The answer is perhaps no but that's irrelevant. I felt a more
proactive approach would involve experimenting with some 'test' cases.
erase, find and find_if seem like some of the more common ones. After
all some containers have their own versions of erase and find. In any
event, for experiment, I started to pick on them.

I've noticed over the years definite "plateaus" of comfort among
programmers.

I know some programers, even after years of C++ experience still rarely
use the standard containers. They would rather roll their own, usually
citing performance as a reason, but sometimes complexity.

Next are the programers who have no problem with the standard
containers, but don't use anything else in the STL, even the algorithms
seem too obfuscated to them.

Then there are the ones (like I suspect Mr. Pibinger) who use some of
the algorithms, but function objects, binders, adapters, and negaters
are just too much "noise".

I'm not saying that these programers are any less skilled, They still do
a good job of satisfying the program requirements. When it comes to the
language though, they have a smaller vocabulary.
 
D

Daniel T.

You learned that already, your original loop doesn't stop once it finds
an element but continues all the way to the end of the loop. But maybe
you wanted that behavior from the beginning? Then I would turn to
something like:

Actually, here's my test scenario and what I'm trying to do:


/*
Given a vector of my_struct1 and a vector of my_struct2.
I'll compare the vector of my_struct1 with my_struct2 for equality:

To do this:
Consider
my_struct1 (in main below) - told me I should have
a segment_id - 5, data_size - 0x100, value - 0xA5.

Well, I'll walk through my_struct2.
In my_struct2, there's a segment_id - 5, data_size - 0x100.

The question then becomes do I have a value of 0xA5 in
my_struct2. To determine this.
I search the address - ptr_addr in my_struct2 to detemine
if the value is 0xA5 for the length of 0x100
*/[/QUOTE]

Compare for equality... Sounds like op==

bool operator==( const my_struct1& lhs, const my_struct2& rhs ) {
ADDR_TYPE rhs_end = rhs.ptr_addr + rhs.data_size;
return lhs.segment_id == rhs.segment_id &&
lhs.data_size == rhs.data_size &&
find( rhs.ptr_addr, rhs_end, lhs.value )
!= rhs_end;
}

bool operator==( const my_struct2& lhs, const my_struct1& rhs ) {
return rhs == lhs;
}

Note how the second op== uses the first one.


Now given a vector full of my_struct2 objects:

void foo( const vector<my_struct2>& vec, const my_struct1& s ) {
vector<my_struct2>::const_iterator it =
find( vec.begin(), vec.end(), s );
if ( it != vec.end() ) {
// found a my_struct2 object that matches 's'
}
else {
// none there
}
}
# include <vector>
# include <iostream>
# include <algorithm>
#include <fstream>

using std::cout;
using std::endl;
using std::vector;
using std::eek:stream;


typedef unsigned int uint_type;
struct my_struct1 {
uint_type segment_id;
uint_type data_size;
uint_type value;
};

typedef unsigned char* ADDR_TYPE;
struct my_struct2 {
uint_type segment_id;
uint_type data_size;
ADDR_TYPE ptr_addr;
};

Get used to classes. As written above any code could conceivably change
the value of any of the elements in an object of either struct. That
could be a real problem, especially in my_struct2 where there is an
invariant that the number of elements in ptr_addr is equal to data_size.
typedef std::vector<my_struct1> MS1;
typedef std::vector<my_struct2> MS2;

int main()
{
my_struct1 ms11, ms12;

The three lines below "construct" a my_struct1 object. Put them in a
constructor of the class.
ms11.segment_id = 5;
ms11.data_size = 0x100;
ms11.value = 0xA5;

MS1 m1;
m1.push_back(ms11);
The three lines below "construct" a my_struct1 object. Put them in a
constructor of the class.
ms12.segment_id = 2;
ms12.data_size = 0x400;
ms12.value = 0xB5;
m1.push_back(ms12);
Why are you using a int* here but then assigning the block of memory
into a unsigned char* below? Try to keep your types alike. If you need a
cast, rethink your design...
int *ptr_addr1 = new int[0x100];
std::fill ( ptr_addr1, ptr_addr1 + 0x100, 0xA5 );

Also, use a vector for the block of memory above, that will make memory
management easer.
my_struct2 ms21, ms22;

The three lines below "construct" a my_struct2 object. Put them in a
constructor of the class.
ms21.segment_id = 5;
ms21.data_size = 0x100;
ms21.ptr_addr = (ADDR_TYPE)ptr_addr1;

MS2 m2;
m2.push_back(ms21);

int *ptr_addr2 = new int[0x400];
std::fill ( ptr_addr2, ptr_addr2 + 0x400, 0xB5 );
The three lines below "construct" a my_struct2 object. Put them in a
constructor of the class.
ms22.segment_id = 2;
ms22.data_size = 0x400;
ms22.ptr_addr = (ADDR_TYPE)ptr_addr2;

m2.push_back(ms22);
delete [] ptr_addr1;
delete [] ptr_addr2;

At this moment, both m21 and m22 have pointers to invalid memory blocks
AND SO DO THE TWO my_struct2 OBJECTS IN THE m2 VECTOR. It doesn't matter
in this case because the program is over anyway, but it's not a good
habit to get into.

Did you know at this point you have four my_struct2 objects?
 
M

ma740988

Compare for equality... Sounds like op==

bool operator==( const my_struct1& lhs, const my_struct2& rhs ) {
ADDR_TYPE rhs_end = rhs.ptr_addr + rhs.data_size;
return lhs.segment_id == rhs.segment_id &&
lhs.data_size == rhs.data_size &&
find( rhs.ptr_addr, rhs_end, lhs.value )
!= rhs_end;
}

bool operator==( const my_struct2& lhs, const my_struct1& rhs ) {
return rhs == lhs;
}

Note how the second op== uses the first one.


Now given a vector full of my_struct2 objects:

void foo( const vector<my_struct2>& vec, const my_struct1& s ) {
vector<my_struct2>::const_iterator it =
find( vec.begin(), vec.end(), s );
if ( it != vec.end() ) {
// found a my_struct2 object that matches 's'
}
else {
// none there
}
}

Got it!!
Get used to classes. As written above any code could conceivably change
the value of any of the elements in an object of either struct. That
could be a real problem, especially in my_struct2 where there is an
invariant that the number of elements in ptr_addr is equal to data_size. Indeed
m2.push_back(ms22);
delete [] ptr_addr1;
delete [] ptr_addr2;

At this moment, both m21 and m22 have pointers to invalid memory blocks
AND SO DO THE TWO my_struct2 OBJECTS IN THE m2 VECTOR. It doesn't matter
in this case because the program is over anyway, but it's not a good
habit to get into.

Did you know at this point you have four my_struct2 objects?
Four? I'm not seeing how
 
D

Daniel T.

Compare for equality... Sounds like op==

bool operator==( const my_struct1& lhs, const my_struct2& rhs ) {
ADDR_TYPE rhs_end = rhs.ptr_addr + rhs.data_size;
return lhs.segment_id == rhs.segment_id &&
lhs.data_size == rhs.data_size &&
find( rhs.ptr_addr, rhs_end, lhs.value )
!= rhs_end;
}

bool operator==( const my_struct2& lhs, const my_struct1& rhs ) {
return rhs == lhs;
}

Note how the second op== uses the first one.


Now given a vector full of my_struct2 objects:

void foo( const vector<my_struct2>& vec, const my_struct1& s ) {
vector<my_struct2>::const_iterator it =
find( vec.begin(), vec.end(), s );
if ( it != vec.end() ) {
// found a my_struct2 object that matches 's'
}
else {
// none there
}
}

Got it!![/QUOTE]

Also, look at the 'find_first_of' algorithm...

vector<my_struct2>::iterator it = find_first_of(m2.begin(), m2.end(),
m1.begin(), m1.end());

'it' will now point to the first 'my_struct2' object in 'm2' that
"equals" any one element in 'm1' (or m2.end() if none exists.)
m2.push_back(ms22);
delete [] ptr_addr1;
delete [] ptr_addr2;

At this moment, both m21 and m22 have pointers to invalid memory blocks
AND SO DO THE TWO my_struct2 OBJECTS IN THE m2 VECTOR. It doesn't matter
in this case because the program is over anyway, but it's not a good
habit to get into.

Did you know at this point you have four my_struct2 objects?
Four? I'm not seeing how

Look at your code again. In main you have an object 'ms21' then you do:

m2.push_back(ms21);

Please note that ms21 is *not* in the m2 vector. push_back places a
*copy* of ms21 in the vector, not the actual object.

&ms21 does *not* equal &m2.back().

The same logic applies for all the other objects you created in main.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,600
Members
45,180
Latest member
CryptoTax Software
Top