Generic Templates in C++ - c++

I don't understand the answers to the following questions:
Write a C++ function find_elem that takes two iterators first and last of some
sequence of elements of type T and an object obj of type T. It returns the iterator to the first occurrence of obj in the range [first, last), or the iterator last if obj is not in the sequence.
This was the answer
template <typename It, typename T>
It find_elem(It first, It last, const T & obj) {
while (first != last && (*first) != obj) // I DON'T UNDERSTAND THIS LINE
++first;
return first;
}
I dont understand the following line while (first != last && (*first) != obj). Why is it (*first != obj) when the questions asks you to return the iterator with the first instance of obj. I also don't get the following line ++first as in why you are incrementing the iterator first

The ++first is executed by the while loop.
I would write use { } here to make it clearer:
while (first!=last && (*first)!=obj) {
++first;
}
So, the while loop checks if (*first)==obj. If not, then it moves to the next element in the list using ++first, which increments the iterator. Then it ends either when first==last (meaning that we have gone through the entire list), or when (*first)==obj, meaning that we found what we were looking for.

A copy of first is passed to the function. This means that the function can safely modify the variable by using it also to iterate the sequence.
It's just a concise alternative for the following code:
template <typename It, typename T>
It find_elem(It first, It last, const T & obj) {
It iterator = first;
while (iterator != last && (*iterator) != obj)
++iterator;
return iterator;
}
By the way... "generic templates" sounds strange, because templates are always generic. I suppose template template parameters could be called "generic templates", though.

Related

Is there an elegant way to return the second found element of std::adjacent_find?

If you want to return the second element of adjacent_find, instead of the first, is there a nice way to solve this without handling the special case that no element is found?
So I would like equivalent code of the following function (such that it is not necessary to write your own function for that):
template<class Range, class Pred>
auto adjacent_find_second(Range&& range, Pred pred)
{
auto it = std::ranges::adjacent_find(range, pred);
if(it == range.end()) { return it; }
else return ++it;
}
The problem is that the iterators that std::adjacent_find can possibly return are not consecutive.
It can return all possible iterators of the range (including end), but not an iterator to the last element.
A reimplementatiton of adjacent_find would be more efficient because it could just return the iterator to the second element in the found pair. This also would not need to do that additional check.
Does anyone have an idea to solve this problem without the check?

Check if iterator to std::map points to second last element

I'm (forward) iterating over a std::map and would like to find if the iterator points to the second last element. I can't seem to find how to do that anywhere.
I've got:
bool
isSecondLastFile(const TDateFileInfoMap::const_iterator &tsFile)
{
TDateFileInfoMap::reverse_iterator secondLastIt = mFileInfoMap.rbegin() + 1;
return (tsFile == secondLastIt);
}
Where TDateFileInfoMap is std::map
I'm getting:
error: no match for ‘operator==’ in ‘tsFile == secondLastIt’
/usr/lib/gcc/i686-redhat-linux/4.4.7/../../../../include/c++/4.4.7/bits/stl_tree.h:287: note: candidates are: bool std::_Rb_tree_const_iterator<_Tp>::operator==(const std::_Rb_tree_const_iterator<_Tp>&) const [with _Tp = std::pair<const long int, TFileInfo>]
Does that mean I can't compare the forward and reverse iterator?
How do I figure out if the forward iterator is pointing at the second last element?
std::map's iterator type is BidirectionalIterator. Just decrement the end iterator twice--first to get the last element since m.end() returns an iterator at the after the end position, and then again to get the second-last element:
auto penultimate = std::prev(m.end(), 2);
Then you can simply check for equality with the resultant iterator:
auto it = m.begin();
it == penultimate;
see it live on Coliru
Naturally, you should check that the map has two elements first if it's not guaranteed by other logic in your program.
Does that mean I can't compare the forward and reverse iterator?
Yes you can't compare them directly.
You can use base() to get the underlying base iterator.
Returns the underlying base iterator. That is
std::reverse_iterator(it).base() == it.
The base iterator refers to the element that is next (from the
std::reverse_iterator::iterator_type perspective) to the element the
reverse_iterator is currently pointing to. That is &*(rit.base() - 1) == &*rit.
e.g.
return (tsFile == (++secondLastIt).base());
BTW: mFileInfoMap.rbegin() + 1 won't compile since the iterator of std::map is not RandomAccessIterator. You might write:
TDateFileInfoMap::reverse_iterator secondLastIt = mFileInfoMap.rbegin();
++secondLastIt;
Note that we're not checking whether the map is empty or has only one element.
A simple solution for forward iterators:
template <typename ForwardIterator>
inline bool isNthLast(std::size_t n, ForwardIterator pos, ForwardIterator last) {
for( ;; --n, ++pos) {
if(n == 0)
return (pos == last);
if(pos == last)
return false;
}
}
bool isSecondLastFile(TDateFileInfoMap::const_iterator sFile) {
return isNthLast(2, sFile, mFileInfoMap.end());
}
Let's say you have a set with name s.
s= {s1,s2,...,sN-1, sN}
Now to iterate from s1.. to sN-1 (which is second last element) we will use STL functions, s.begin() and s.end().
end = s.end(); //end points to end
end--// end points to sN
Now in the for loop when itr (starts from the beginning of set) becomes equal to sN the loop will break, and you will get s1,s2,..sN-1 inside the loop.
map<int,int> s;
// to iterate till fixed range in map
auto end =s.end();
end--; // end to second last;
for(auto itr = s.begin(); itr!=end;itr++){
// do your operation
}

How to use recursion in std::list?

I am having trouble executing this block of code. The first base condition of this code(for searching a string in a list of strings) does not work. Thanks.
int string_check(list<string> l,list<string>::iterator it,string s)
{
if(it==l.end()) return 0;
if(*it==s) return 1;
return(string_check(l,++it,s));
}
You're passing the list by value, so l.end() is the end of a different list each time, and never the one that it came from.
Either pass the list by reference; or pass the end iterator rather than the list itself. That would be a more flexible solution, allowing you to decouple the function from a specific container type and support any range of input iterators:
template <typename InIter, typename T>
bool contains(InIter begin, InIter end, T const & value) {
if (begin == end) return false;
if (*begin == value) return true;
return contains(++begin, end, value);
}
Recursion is often a bad idea, as the stack is typically fairly small and causes horrible bugs if it overflows. Unless this is an exercise in implementing such a function, use iteration:
for (; begin != end; ++begin) {
if (*begin == value) return true;
}
return false;
or the standard library:
return std::find(begin, end, value) != end;
You need to write int string_check(const list<string>& l, const list<string>::iterator it&, const string& s) instead.
Else you're taking a value copy of the std::list and the iterator, so any modifications to them will not be reflected in the caller.
I'm passing the objects by constant reference. This helps program stability since the function body cannot modify the parameters passed. I'm also passing the string in this way too in order to prevent an unnecessary value copy.
I would define the function differently. It is enough to specify a pair of iterators that specify the target range in the list.
bool string_check( std::list<std::string>::const_iterator first,
std::list<std::string>::const_iterator last,
const std::string &s )
{
return ( first != last ) &&
( *first == s || string_check( ++first, last, s ) );
}
That is there is no need to pass also the list itself.
The same way you could define a template function that could deal with any type of the list. Only the name of the function you should select more suitable.
For example
template <class InputIterator, class T>
bool find( InputIterator first,
InputIterator last,
const T &value )
{
return ( first != last ) &&
( *first == value || string_check( ++first, last, value ) );
}
As for your function implementation then the first parameter that is the list shall be be declared as reference
list<string> &l
Moover it should be declared as a const reference
const list<string> &l
that the function could be called for constant list. In this case you have also to change the type of the iterator.

Returned iterator from method like std::find_if does not match regular iterator

I have this std::find_if() like method that returns found iterator (that matches "condition" functor).
template<class T, class Function>
typename std::set<T>::iterator setFindIf(set<T> set, Function condition) {
typename std::set<T>::iterator iterator = set.begin();
for (; iterator != set.end(); iterator++) {
cout<<"U";
if (condition(*iterator)) {
break;
}
}
return iterator;
}
And this line that calls it:
std::set<Order>::iterator it = setFindIf(orders, orderCustomerHasOpenOrder(id, ordNum));
I'm testing on an empty set, so this line (that comes right after the above line) should print '1':
cout<<(it==orders.end());
Why doesn't this work? when I add this line at the end of the setFindIf() method, it prints '1' as expected.
You're taking your set in by value. So it's a copy of the container that you passed in. Comparing those iterators is undefined behavior, because they belong to different containers. Pass the set in by reference instead.

Exam ques regarding iterators and generic functions

I'm attempting to solve the below exam question but I'm having difficulties with it.
Write a C++ function find_elem that takes two iterators first and last
of some sequence of elements of type T and an object obj of type T. It
returns the iterator to the first occurrence of obj in the range
(first, last), or the iterator last if obj is not in the sequence.
(35%)
NOTE: first & last are not necessarily the same as what is returned by
a container’s begin() and end() methods! The only thing we suppose is
that the container is some sort of a sequence (e.g., vector, list,
etc.) and that first is an iterator which points to an element which
comes before the one pointed to by last. You must not dereference last
because it might be the result of end()!
Here's my attempt
template<typename Iter, typename Obj>
Iter find_element(Iter iter1, Iter iter2, Obj &obj){
for(p = iter1; p != iter2; p++){
if((*p) == obj){
return p;
}
return iter2;
}
}
Is this attempt correct? Is the return type suitable for the function or have I got the wrong idea?
Yes, your code is correct. I would perhaps only change Obj &obj to Obj const &obj.
And you ought to have declared p.
More nitpicking: with generic iterators usually the form ++p is preferred.
My variant (basically the same):
template<typename Iter, typename Obj>
Iter find_element(Iter iter1, Iter iter2, Obj const &obj)
{
for(; iter1 != iter2; ++iter1)
{
if(*iter1 == obj)
break;
}
return iter1;
}