2
votes

I'm stuck when doing an exercise from the book C++ Primer , which goes:

Exercise 10.24:Use bind and check_size to find the first element in a vector of ints that has a value greater than the length of a specified string value.

My code:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>

bool check_size(const std::string &s, std::string::size_type sz)
{
   return s.size() > sz;
}

std::vector<int>::iterator
find_first_bigger(const std::vector<int> &v, const std::string &s);

int main(){return 0;}

std::vector<int>::iterator
find_first_bigger(const std::vector<int> &v, const std::string &s)
{
    auto it= std::find_if_not(v.begin(), v.end(),std::bind(check_size,s,std::placeholders::_1));
    return it;
}

When trying to compile, the compiler complained that:

error: could not convert 'it' from '__gnu_cxx::__normal_iterator<const int*, std::vector<int> >' to 'std::vector<int>::iterator {aka __gnu_cxx::__normal_iterator<int*, std::vector<int> >}'

To find out what is going on,I turned to the header *stl_algo.h* in which I guess std::find_if_not is defined.The comment there said:

@return The first iterator @c i in the range @p [_first,_last)

The definition of this function:

template<typename _InputIterator, typename _Predicate>
inline _InputIterator
find_if_not(_InputIterator __first, _InputIterator __last,
    _Predicate __pred)
{
  // concept requirements
  __glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
  __glibcxx_function_requires(_UnaryPredicateConcept<_Predicate,
      typename iterator_traits<_InputIterator>::value_type>)
  __glibcxx_requires_valid_range(__first, __last);
  return std::__find_if_not(__first, __last, __pred);
}

It seems the return type should be the same to the argument.But why complier complained so? How to fix this error?Thx.

2
iterator isn't the same as const_iterator. You can't get an iterator from a constant vector. - chris
Another abuse of auto? auto is fine for the control variable of a for loop, where the variables declared don't have scope outside of the for loop, but it's use more generally should be considered an anti-pattern. - James Kanze
@chris Thanks..you are right.. - Yue Wang
@JamesKanze So..do you mean this way is better: std::vector<int>::iterator it= std::find_if_not(v.begin(), v.end(),std::bind(check_size,s,std::placeholders::_1)); ? If so, can you tell me more why this way is better? I just began learning C++, really need this kind of suggestion. - Yue Wang
@AlanWang In this case, definitely yes. The type is important here (and you would have gotten the error on the initialization of it), and you should specify it, even if it is a bit verbose. About the only time where you want to use auto is in hand written loops, where the type doesn't propagate, and the variable is only used as a classic iterator (comparison with container.end(), dereference, increment). - James Kanze

2 Answers

4
votes
std::vector<int>::iterator
find_first_bigger(const std::vector<int> &v, const std::string &s);
                   ^^

Your vector is const. So you'll get a std::vector<int>::const_iterator. Had it not been const, you'd get a std::vector<int>::iterator

3
votes

Notice

//...
inline _InputIterator
find_if_not(_InputIterator __first, _InputIterator __last,
    _Predicate __pred)
//...

returns the same type of iterator that it receives as a parameter.

Since you passed a const std::vector<int> &v you must return a const_iterator thus

std::vector<int>::const_iterator
find_first_bigger(const std::vector<int> &v, const std::string &s);