Containers are required to provide an iterator
type which is implicitly convertible to a const_iterator
. Given this, I am trying to use auto
to initialize an object via vector::begin()
, and use that resulting object in std::distance
where the RHS is a const_iterator
. This isn't working. Here is a complete example:
#include <cstdlib>
#include <vector>
#include <iterator>
#include <iostream>
typedef std::vector <char> Packet;
typedef std::vector <Packet> Packets;
template <typename Iter>
Iter next_upto (Iter begin, Iter end, size_t n)
{
Iter ret = begin;
for (; n > 0 && ret != end; ++ret, --n)
;
return ret;
}
Packets::const_iterator Process (Packets::const_iterator begin, Packets::const_iterator end)
{
Packets::const_iterator ret = begin;
while (ret != end)
++ret; // do something
return ret;
}
int main()
{
Packets test (100); // vector of 100 default-initialized packets
// process them 10 at a time
for (auto it = test.begin();
it != test.end();
it = next_upto (it, test.end(), 10))
{
auto itr = Process (it, next_upto (it, test.end(), 10));
Packets::const_iterator it2 = it;
const size_t n1 = std::distance (it2, itr);
const size_t n = std::distance (it, itr);
std::cout << "Processed " << n << " packets\n";
}
}
Under g++ 4.8.1 (and 4.8.2) compiling this yields:
[1/2] Building CXX object CMakeFiles/hacks.dir/main.o
FAILED: /usr/bin/g++ -Wall -std=c++11 -g -MMD -MT CMakeFiles/hacks.dir/main.o -MF "CMakeFiles/hacks.dir/main.o.d" -o CMakeFiles/hacks.dir/main.o -c main.cpp
main.cpp: In function ‘int main()’:
main.cpp:39:45: error: no matching function for call to ‘distance(__gnu_cxx::__normal_iterator<std::vector<char>*, std::vector<std::vector<char> > >&, __gnu_cxx::__normal_iterator<const std::vector<char>*, std::vector<std::vector<char> > >&)’
const size_t n = std::distance (it, itr);
^
main.cpp:39:45: note: candidate is:
In file included from /usr/include/c++/4.8/bits/stl_algobase.h:66:0,
from /usr/include/c++/4.8/vector:60,
from main.cpp:2:
/usr/include/c++/4.8/bits/stl_iterator_base_funcs.h:114:5: note: template<class _InputIterator> typename std::iterator_traits<_Iterator>::difference_type std::distance(_InputIterator, _InputIterator)
distance(_InputIterator __first, _InputIterator __last)
^
/usr/include/c++/4.8/bits/stl_iterator_base_funcs.h:114:5: note: template argument deduction/substitution failed:
main.cpp:39:45: note: deduced conflicting types for parameter ‘_InputIterator’ (‘__gnu_cxx::__normal_iterator<std::vector<char>*, std::vector<std::vector<char> > >’ and ‘__gnu_cxx::__normal_iterator<const std::vector<char>*, std::vector<std::vector<char> > >’)
const size_t n = std::distance (it, itr);
^
I'm aware that I can fix this particular instance by calling cbegin()
and cend()
rather than begin()
and end()
, but since begin()
and end()
return a type that should be convertible to const_iterator
, I'm not sure I understand why this is needed.
Why does auto
deduce a type in this case that is not convertible to const_iterator
?
Your problem can be reduced to the following example, which fails for the same reasons.
std::distance
is defined using the same template parameter type for both arguments, and template argument deduction is failing because you have aconst_iterator
anditerator
.User defined conversions are not considered when deducing template arguments from function calls, and since the two arguments have different types in this case, and both are participating in template argument deduction, the deduction fails.
§14.8.1/6 [temp.arg.explicit]
§14.8.2.1/4 [temp.over]
You'll need to convert the
iterator
toconst_iterator
, or specify the template argument tostd::distance
explicitly.or
Other options are, of course, to not use
auto
and explicitly specify the iterator type in both cases, or use thevector::cbegin()
andvector::cend()
member functions when you need to ensure that the type is aconst_iterator
.begin()
:Signatures:
You declared your vector as
Packets test (100);
, which is non const.If you declare it const,
auto
type deduction will have the secondbegin()
overload as a best (and unique) match.It compiles and run with this simple fix.
The problem has nothing common with the iterator conversion. The compiler simply unable to determine the template argument. It is the same if you would write
though an object of type
int
can be implicitly converted to an object of typelong
.As for your example you could write