#c #const-cast
#c #приведение константы
Вопрос:
я наткнулся на нечто похожее на приведенный ниже фрагмент кода, который выдает ошибку компилятора из-за использования const_iterator
. есть ли причина, по которой vec.end()
in std::copy
неявно получает const
приведение?
int main(int argc, char* argv[]) {
vector<int> vec;
vec.push_back(20);
vec.push_back(30);
vector<int> copy_vec;
vector<int>::const_iterator i = vec.begin();
std::copy(i,vec.end(),back_inserter(copy_vec));
cerr<<copy_vec.size()<<endl;
return 0;
}
Ответ №1:
Нет необходимости в std::copy
, вам также не нужна пара итераторов.
Просто сделайте это:
vector<int> copy_vec(vec); //use the copy constructor!
И готово!
Что касается вашего кода, то причина, по которой он выдает ошибку, заключается в том, что первый итератор для std::copy
является const_iterator
, но второй итератор просто iterator
. Оба должны быть одного типа, но это не так, и из-за этого вывод аргумента шаблона завершается неудачей для std::copy
, который является шаблоном функции.
Чтобы понять это на примере, рассмотрим этот простой код:
template<typename T>
void f(T a, T b) {}
int main()
{
int a = 100;
char b = 'A';
f(a,b);
}
Выдает ошибку (см. ideone):
prog.cpp:8: error: no matching function for call to ‘f(intamp;, charamp;)’
Это не компилируется, потому что мы полагаемся на вывод аргумента шаблона. Поскольку тип первого параметра и второго параметра в шаблоне функции точно одинаковы, но мы вызываем эту функцию, передавая a
(который является int
) в качестве первого аргумента и b
(который является char
) в качестве второго аргумента, он не может выводить T
однозначно из аргументов разных типов! Пожалуйста, обратите внимание, что преобразование не учитывается при вычете аргумента шаблона.
Однако, если мы не будем полагаться на вывод аргумента шаблона, а вместо этого предоставим аргумент шаблона явно, тогда это сработает (см. ideone):
f<int>(a,b); //works!
Это работает, поскольку нет необходимости выводить T
из аргументов функции!
Аналогично, если вы предоставите аргумент template для std::copy
, то даже ваш код будет работать (см. ideone):
std::copy<vector<int>::const_iterator>(i,vec.end(),back_inserter(copy_vec));
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^ explicitly provide template argument!
Это работает, потому что iterator
может преобразовать в const_iterator
, но const_iterator
не может преобразовать в iterator
, что означает, что следующее выдаст ошибку (см. ideone):
std::copy<vector<int>::iterator>(i,vec.end(),back_inserter(copy_vec));
//^^^^^^^^^^^^^^^^^^^^^ non-const iterator!
Комментарии:
1. на самом деле выше приведен всего лишь пример кода, меня больше интересовало выяснить, почему неявное преобразование const не происходит
2. @keety: Я также объяснил эту часть. 🙂
3. @keety: Нет преобразования типов при зависящем от аргумента поиске (ADL) для функций шаблона. Тип данных должен точно совпадать.
4. @keety: Я также добавил больше объяснений на простом примере!
Ответ №2:
vec
является неконстантным вектором, и поэтому end
вернет неконстантный итератор. Такой итератор на самом деле не может быть неявно преобразован в версию const, потому что они являются отдельными классами.
Вместо этого используйте конструктор с двумя итераторами:
vector<int> copy_vec(vec.begin(), vec.end());
Комментарии:
1. Почему не просто
vector<int> copy_vec(vec)
? 😛2. Вы ошибаетесь в этом >>
Such an iterator actually cannot be implicitly converted to a const version, because they're separate classes.
Это не причина, по которой это не работает (смотрите мой ответ по реальной причине). Кроме того, неконстантный итератор может неявно преобразовываться в const-итератор для всех типов стандартных контейнеров.
Ответ №3:
Я думаю, что проблема здесь в том, что std::copy объявлен для принятия трех параметров.
Первые два имеют один и тот же тип, и вы передаете ( const_iterator, vec.end () )
— и vec.end() возвращает неконстантный итератор (потому что vec является неконстантным вектором). Это приводит к тому, что компилятор отклоняет шаблон для std::copy.
Если бы у вас была функция, подобная этой:
void doit ( const std::vector<int> amp;vec, std::vector<int> amp;out ) {
std::vector<int>::const_iterator i = vec.begin();
std::copy(i,vec.end(),back_inserter(out));
}
Затем он скомпилируется и будет работать нормально, потому что vec.end () вернет const_iterator .
В качестве альтернативы, если вы используете C 11, вы можете вызвать vec.cend (), чтобы получить const_iterator .