Контейнеры обязаны предоставлять iterator
тип, который неявно преобразуется в const_iterator
, Учитывая это, я пытаюсь использовать auto
инициализировать объект через vector::begin()
и использовать этот результирующий объект в std::distance
где RHS является const_iterator
, Это не работает Вот полный пример:
#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";
}
}
Под g ++ 4.8.1 (и 4.8.2) компилирование приводит к:
[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);
^
Я знаю, что могу исправить этот конкретный экземпляр, позвонив cbegin()
а также cend()
скорее, чем begin()
а также end()
, но с тех пор begin()
а также end()
вернуть тип, который должен быть преобразован в const_iterator
Я не уверен, что понимаю, зачем это нужно.
Почему auto
в этом случае выведите тип, который не конвертируется в const_iterator
?
Ваша проблема может быть сведена к следующему примеру, который терпит неудачу по тем же причинам.
#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
определяется с использованием одного и того же типа параметра шаблона для обоих аргументов, и вывод аргумента шаблона не выполняется, потому что у вас есть const_iterator
а также iterator
,
Определенные пользователем преобразования не учитываются при выводе аргументов шаблона из вызовов функций, и, поскольку в этом случае два аргумента имеют разные типы, и оба участвуют в выводе аргументов шаблона, вывод не выполняется.
§14.8.1 / 6 [temp.arg.explicit]
Неявное преобразование (раздел 4) будет выполнено для аргумента функции, чтобы преобразовать его в тип соответствующего параметра функции, если тип параметра не содержит Шаблон-параметры которые участвуют в выводе аргумента шаблона.
§14.8.2.1 / 4 [temp.over]
...
[ Замечания: как указано в 14.8.1, неявные преобразования будут выполняться для аргумента функции, чтобы преобразовать его в тип соответствующего параметра функции, если параметр не содержит Шаблон-параметры которые участвуют в выводе аргумента шаблона. Такие преобразования также допускаются в дополнение к описанным в предыдущем списке. —Конечная записка ]
Вам нужно будет преобразовать iterator
в const_iterator
или укажите аргумент шаблона для std::distance
в явном виде.
auto n = std::distance(it1, static_cast<decltype(it1)>(it2));
или же
auto n = std::distance<decltype(it1)>(it1, it2);
Другие варианты, конечно, не использовать auto
и явно укажите тип итератора в обоих случаях, или используйте vector::cbegin()
а также vector::cend()
функции-члены, когда вам нужно убедиться, что тип const_iterator
,
Проблема не имеет ничего общего с преобразованием итератора. Компилятор просто не может определить аргумент шаблона. То же самое, если бы вы написали
int x = 10;
long y = 20;
std::cout << std::max( x, y ) << std::endl;
хотя объект типа int
может быть неявно преобразован в объект типа long
,
Что касается вашего примера, вы могли бы написать
const size_t n = std::distance<std::vector<char>::const_iterator> (it, itr);
Почему в этом случае auto выводит тип [e, который не конвертируется в const_iterator?
begin()
:Подписи:
iterator begin();
const_iterator begin() const;
Вы объявили свой вектор как Packets test (100);
, который не является постоянным.
Если вы объявите это const, auto
Тип вычета будет иметь второе begin()
перегрузка как лучший (и уникальный) матч.
Он компилируется и запускается с этим простым исправлением..