2

There are times when I'll want to use the iterator returned by a function from the algorithm library. The problem I'm facing occurs when I go between a modifying function and a non-modifying function. Cause in the non-modifying function I want to use a const_iterator. As a toy example:

vector<int> v = { 1, 8, 7, 4, 3, 6, 2, 5 }; auto it = partition(begin(v), end(v), bind(greater<int>(), placeholders::_1, 3)); cout << (find(cbegin(v), it, 13) != cend(v)) << endl; 

When I try to compile this code I get the error:

no matching function for call to find(std::vector<int>::const_iterator, __gnu_cxx::__normal_iterator<int*, std::vector<int> >&, int)

The problem I'm running into is the only conversion process I can find is potentially expensive: auto cit = next(cbegin(v), distance(begin(v), it))

Is there a way I can make this work? Or am I stuck converting or just using non-const_iterators?

10
  • Just a preemptive comment, I know that I could just use find directly on v. I'm not looking for a better way to write my toy example; I'm looking for a solution to the problem it illustrates. Commented Feb 16, 2017 at 20:11
  • 1
    All STL container iterator are convertible to its const_iterator. See this Commented Feb 16, 2017 at 20:14
  • 2
    Maybe using std::list would be better for the toy example, otherwise assertions such as distance being expensive don't hold. Commented Feb 16, 2017 at 20:16
  • 1
    @JonathanMee something like this? stackoverflow.com/questions/765148/… Commented Feb 16, 2017 at 20:49
  • 1
    nice link - Scott Meyers have not included it in item 13 of his Effective Modern C++. He states that in c++11 conversion of const_iterator to iterator is UB. But he mentions that there are ways to accomplish that but they are not worth including in the book. Commented Feb 16, 2017 at 21:00

3 Answers 3

4

You can specify the template argument:

find<decltype(cbegin(v))>(cbegin(v), it, 13) != cend(v) 

Demo

Sign up to request clarification or add additional context in comments.

1 Comment

I see the error is actually because I'm cause ambiguity in the template deduction of find. So this is the same as the other examples, forcing a cast. Is there a statement anywhere that such a cast is legal, and not just implementation defined?
3

It's much less expensive to simply cast the mutable iterator to a constant iterator:

cout << (find(cbegin(v), vector<int>::const_iterator{it}, 13) != cend(v)) << endl; 

A mutable iterator should always be castable into a constant iterator.

EDIT: I found the part of the standard that guarantees that an iterator is convertible to a constant iterator.

Table 96 in section 23.2 "Container requirements" specifies that the expression X::iterator results in:

any iterator category that meets the forward iterator requirements. convertible to X::const_iterator.

6 Comments

Is there a guaranteed such conversion?
@Cheersandhth.-Alf See here: stackoverflow.com/a/7759474/1294207
@FantasticMrFox: That's fantastic, thanks. I sort of trust the Good Robot. But a standard ref. would have clinched the matter.
"Note: iterator and const_-iterator have identical semantics in this case, and iterator is convertible to const_iterator." - I found this wording explicitly copy-pasted between 23.2.4, associative containers, and 23.2.5 unordered associative containers. Curiously it is missing from 23.3, sequence containers, which seems to be an oversight, unless it is worded slightly differently.
@JonathanMee -- found the cite from the standard, that seems to apply to all iterator types.
|
1

There are three approaches.

The first one is to write

cout << (find( begin(v), it, 13) != cend(v)) << endl; ^^^^^ 

The second one is to write

cout << (find(cbegin(v), static_cast<std::vector<int>::const_iterator>( it ) , 13) != cend(v)) << endl; 

Or shorter

cout << (find(cbegin(v), static_cast<decltype( v.cbegin())>( it ) , 13) != cend(v)) << endl; 

And the third one is to write

cout << (find<std::vector<int>>::const_iterator>( cbegin(v), it, 13) != cend(v)) << endl; 

or shorter

cout << (find<decltype( v.cbegin())>( cbegin(v), it, 13) != cend(v)) << endl; 

2 Comments

Yeah i mention in the question that 1 is undesirable. The other 2 options are both effectively casting to a const_iterator (which I didn't know you could do.) Is there anything in the standard that explicitly allows this?
@JonathanMee See in the Table 96 — Container requirements the row about iterators.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.