неявное приведение константы в шаблонах

#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 .