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.
#include <vector>
#include <iterator>
int main()
{
std::vector<int> v;
std::vector<int>::const_iterator it1 = v.begin();
auto it2 = v.end();
auto n = std::distance(it1, it2);
}
std::distance
is defined using the same template parameter type for both arguments, and template argument deduction is failing because you have a const_iterator
and iterator
.
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]
Implicit conversions (Clause 4) will be performed on a function argument to convert it to the type of the corresponding function parameter if the parameter type contains no template-parameters that participate in template argument deduction.
§14.8.2.1/4 [temp.over]
...
[ Note: as specified in 14.8.1, implicit conversions will be performed on a function argument to convert it to the type of the corresponding function parameter if the parameter contains no template-parameters that participate in template argument deduction. Such conversions are also allowed, in addition to the ones described in the preceding list. —end note ]
You'll need to convert the iterator
to const_iterator
, or specify the template argument to std::distance
explicitly.
auto n = std::distance(it1, static_cast<decltype(it1)>(it2));
or
auto n = std::distance<decltype(it1)>(it1, it2);
Other options are, of course, to not use auto
and explicitly specify the iterator type in both cases, or use the vector::cbegin()
and vector::cend()
member functions when you need to ensure that the type is a const_iterator
.
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
int x = 10;
long y = 20;
std::cout << std::max( x, y ) << std::endl;
though an object of type int
can be implicitly converted to an object of type long
.
As for your example you could write
const size_t n = std::distance<std::vector<char>::const_iterator> (it, itr);
Why does auto deduce a typ[e in this case that is not convertible to const_iterator?
begin()
:Signatures:
iterator begin();
const_iterator begin() const;
You declared your vector as Packets test (100);
, which is non const.
If you declare it const, auto
type deduction will have the second begin()
overload as a best (and unique) match.
It compiles and run with this simple fix.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With