Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

auto it = vector.begin() resulting type is not convertible to const_iterator

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?

like image 890
John Dibling Avatar asked Aug 07 '14 14:08

John Dibling


3 Answers

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.

like image 185
Praetorian Avatar answered Oct 09 '22 01:10

Praetorian


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);
like image 21
Vlad from Moscow Avatar answered Oct 09 '22 01:10

Vlad from Moscow


Why does auto deduce a typ[e in this case that is not convertible to const_iterator?

  • You have two available overloads for 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.

like image 2
quantdev Avatar answered Oct 08 '22 23:10

quantdev