Connecting Tech Pros Worldwide Forums | Help | Site Map

Unexpected behavior in STL "includes" algorithm

Generic Usenet Account
Guest
 
Posts: n/a
#1: Nov 2 '05
I am seeing some unexpected behavior while using the STL "includes"
algorithm. I am not sure if this is a bug with the template header
files in our STL distribution, or if this is how it should work.

For your benefit, let me first quote from the STL Standard Reference
(Stepanov & Lee):

template <class InputIterator1, class InputIterator2>
bool includes(InputIterator1 first1, InputIterator1
last1,InputIterator2 first2, InputIterator2 last2);

template <class InputIterator1, class InputIterator2, class Compare>
bool includes(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2, Compare comp);


includes returns true if every element in the range [first2, last2) is
contained in the range [first1, last1). It returns false otherwise.

At most ((last1 - first1) + (last2 - first2)) * 2 - 1 comparisons are
performed.




I have been able to reproduce my problem with a much more simplified
example. Here's my sample source code:

////////////////// SRC BEGIN /////////////////////////
#include <iostream>
#include <vector>
#include <iterator>

using namespace std;

#define TRACE_STMT cout << "\nLine: " <<__LINE__ << endl;

int
main()
{
vector<int> v1;
vector<int> v2;

for(int i = 1; i < 7; i++)
v1.push_back(i);

TRACE_STMT
copy(v1.begin(), v1.end(), ostream_iterator<int> (cout, " "));

for(int i = 3; i < 6; i++)
v2.push_back(i);

vector<int>::iterator itor;

for(itor = v1.begin(); itor != v1.end(); itor++)
if(!includes(v2.begin(), v2.end(), itor, itor))
v1.erase(itor);

TRACE_STMT
copy(v1.begin(), v1.end(), ostream_iterator<int> (cout, " "));

return 0;
}
////////////////// SRC END /////////////////////////


I am expecting elements 3, 4 & 5 to be erased from vector v1, but that
is not happening. The only thing "wierd" about this example is that
"first2" and "last2" have the same value here. However, the STL
Reference Manual does not mention that "first2" and "last2" have to be
distinct.

Any pointers will be appreciated....


Thanks,
Gus


Valentin Samko
Guest
 
Posts: n/a
#2: Nov 2 '05

re: Unexpected behavior in STL "includes" algorithm


Generic Usenet Account wrote:[color=blue]
> int
> main()
> {
> vector<int> v1;
> vector<int> v2;
>
> for(int i = 1; i < 7; i++)
> v1.push_back(i);
>
> TRACE_STMT
> copy(v1.begin(), v1.end(), ostream_iterator<int> (cout, " "));
>
> for(int i = 3; i < 6; i++)
> v2.push_back(i);
>
> vector<int>::iterator itor;
>
> for(itor = v1.begin(); itor != v1.end(); itor++)
> if(!includes(v2.begin(), v2.end(), itor, itor))
> v1.erase(itor);[/color]
Once you call std::vector erase member function, all the iterators may be invalidated.
I.e. you have UB here.

Also, the second range you pass to std::includes is empty, thus the result.

--

Valentin Samko - http://www.valentinsamko.com
usenet@sta.samsung.com
Guest
 
Posts: n/a
#3: Nov 2 '05

re: Unexpected behavior in STL "includes" algorithm


Valentin Samko wrote:
[color=blue]
> Once you call std::vector erase member function, all the iterators may be invalidated.
> I.e. you have UB here.
>[/color]

Sets show the exact same bahavior. Are iterators invalidated upon
calling the erase member function for sets as well? (That may very
well be true, but that is not what Josuttis' book says)
[color=blue]
> Also, the second range you pass to std::includes is empty, thus the result.
>
> --
>[/color]

Why would the range itor-itor be considered empty? (Not a challenge,
just a query)

Thanks,
Gus

Jonathan Mcdougall
Guest
 
Posts: n/a
#4: Nov 2 '05

re: Unexpected behavior in STL "includes" algorithm


Generic Usenet Account wrote:[color=blue]
> I am seeing some unexpected behavior while using the STL "includes"
> algorithm. I am not sure if this is a bug with the template header
> files in our STL distribution, or if this is how it should work.
>
> For your benefit, let me first quote from the STL Standard Reference
> (Stepanov & Lee):
>
> template <class InputIterator1, class InputIterator2>
> bool includes(InputIterator1 first1, InputIterator1
> last1,InputIterator2 first2, InputIterator2 last2);
>
> template <class InputIterator1, class InputIterator2, class Compare>
> bool includes(InputIterator1 first1, InputIterator1 last1,
> InputIterator2 first2, InputIterator2 last2, Compare comp);
>
>
> includes returns true if every element in the range [first2, last2) is
> contained in the range [first1, last1). It returns false otherwise.
>
> At most ((last1 - first1) + (last2 - first2)) * 2 - 1 comparisons are
> performed.
>
>
>
>
> I have been able to reproduce my problem with a much more simplified
> example. Here's my sample source code:
>
> ////////////////// SRC BEGIN /////////////////////////
> #include <iostream>
> #include <vector>
> #include <iterator>
>
> using namespace std;
>
> #define TRACE_STMT cout << "\nLine: " <<__LINE__ << endl;
>
> int
> main()
> {
> vector<int> v1;
> vector<int> v2;
>
> for(int i = 1; i < 7; i++)
> v1.push_back(i);
>
> TRACE_STMT
> copy(v1.begin(), v1.end(), ostream_iterator<int> (cout, " "));
>
> for(int i = 3; i < 6; i++)
> v2.push_back(i);
>
> vector<int>::iterator itor;
>
> for(itor = v1.begin(); itor != v1.end(); itor++)
> if(!includes(v2.begin(), v2.end(), itor, itor))[/color]

Watch out, includes works with ranges and ranges in the C++ standard
library are open-ended. That means it does not include the last element
of the range. If both the begin and end positions are the same, the
range is empty.

This is useful. If a collection is empty, begin() == end(). You can
iterate from begin(), while itor != end(). Just imagine if end()
returned something else.
[color=blue]
> v1.erase(itor);[/color]

itor is invalidated by erase. Do

for(itor = v1.begin(); itor != v1.end(); /*nothing here*/ )
{
if(!includes(v2.begin(), v2.end(), itor, itor+1))
{
v1.erase(itor++);
}
else
{
++itor;
}
}
[color=blue]
> TRACE_STMT
> copy(v1.begin(), v1.end(), ostream_iterator<int> (cout, " "));
>
> return 0;
> }[/color]


Jonathan

Ali Çehreli
Guest
 
Posts: n/a
#5: Nov 2 '05

re: Unexpected behavior in STL "includes" algorithm


<usenet@sta.samsung.com> wrote in message
news:1130969763.335871.317910@z14g2000cwz.googlegr oups.com...[color=blue]
> Valentin Samko wrote:
>[color=green]
>> Once you call std::vector erase member function, all the iterators may be
>> invalidated.
>> I.e. you have UB here.[/color][/color]

More specifically, "Invalidates all the iterators and references after the
point of the erase." (23.2.4.2/3). Any operation that may resize the vector
invalides all the iterators though...
[color=blue][color=green]
>>[/color]
>
> Sets show the exact same bahavior. Are iterators invalidated upon
> calling the erase member function for sets as well? (That may very
> well be true, but that is not what Josuttis' book says)[/color]

No, erasing an element of an associative container (e.g. set) and also
std::list; invalidates only the iterator(s) pointing the erased element(s).
(23.1.2/8)
[color=blue][color=green]
>> Also, the second range you pass to std::includes is empty, thus the
>> result.
>>
>> --
>>[/color]
>
> Why would the range itor-itor be considered empty?[/color]

That's how the ranges work. The second iterator is "one past the end" of the
range; i.e. it's out of the range.

Think about passing such a range to a linear algorithm:

template <class Iter>
void my_algorithm(Iter begin, Iter end)
{
for ( ; begin != end; ++begin)
{
/* ... */
}
}

Ali

Closed Thread