Почему QVector:: содержит ожидает указатель на неконстантный ТИП в качестве его параметра? - PullRequest
3 голосов
/ 04 ноября 2011

Я использую QVector для хранения указателей на объекты, скажем, FYPE *, в моей программе.

    class TYPE {
        // ....
    };
    const TYPE *ptrToCst;
    QVector<TYPE*> qtVct;
    // ...
    if (qtVct.contains(ptrToCst)) { // Error!!!
        // ....
    }

Компилятор говорит, что QVector :: содержит ожидание TYPE * вместо const TYPE * в качестве параметра. Операция const_cast решит эту проблему. Но это не имеет никакого смысла для меня, так как метод содержимого никогда не должен изменять то, на что указывает указатель. И эквивалентный код, использующий вектор STL, работает как положено.

    std::vector<TYPE*>  stlVct;
    // ...
    if (std::find(stlVct.begin(), stlVct.end(), ptrToCst)) { // Ok
        // ...
    }

В чем причина этой разницы? Обрабатывал ли STL контейнеры, которые содержат указатели, таким образом, что std :: find принимает указатель на объект const? Я предполагаю, что частичная специализация шаблона была задействована?

Ответы [ 2 ]

2 голосов
/ 04 ноября 2011

На самом деле это довольно интересный вопрос о правильности констант и некоторых неинтуитивных ошибках, но компилятор прав, отвергая код.

Почему не работает в Qt?

Компилятор отклоняет код, потому что это нарушит правильность кода.Я не знаю библиотеку, но думаю, что могу смело предположить, что сигнатура содержит QVector<T>::contains( T const & value ) [1] , что в случае T == type* означает:

QVector<type *>::contains( type * const & value )

Теперь проблема в том, что вы пытаетесь передать переменную типа type const *.Проблема в том, что компилятор не знает, что contains делает внутри, только обещания, которые он предлагает в своем интерфейсе.contains обещает не изменять указатель , но ничего не говорит о pointee .В подписи нет ничего, что мешало бы реализации contains изменить значение.Рассмотрим этот похожий пример:

void f( int * const & p ) { // I promise not to change p
   *p = 5;                  // ... but I can modify *p
}
int main() {
   const int k = 10;
   int const * p = &k;
   f( p );                  // Same problem as above: if allowed, f can modify k!!!
}

Почему он разрешает аналогичный вызов std::find тогда?

Разница с std::find в том, что findшаблон, в котором разные типы аргументов имеют очень слабую связьСтандартная библиотека не выполняет проверку типов в интерфейсе, а выполняет реализацию шаблона.Если аргумент не имеет правильного типа, он будет выбран при создании экземпляра шаблона.

Это означает, что реализация будет выглядеть примерно так:

template <typename Iterator, typename T>
Iterator find( Iterator start, Iterator end, T const & value ) {
   while ( start != end && *start != value )
      ++start;
   return start;
}

Тип итераторов и значение полностью не связаны, там нет никаких ограничений, кроме тех, которые накладываются кодом внутри шаблона.Это означает, что вызов будет соответствовать аргументам Iterator == std::vector<type*>::iterator и T = type const *, и подпись будет совпадать.Поскольку внутреннее значение используется только для сравнения с *start, и допустимо сравнение type * и type const * const (оно преобразует первое в позднее и затем сравнивает [2] ) кодотлично компилируется.

Если шаблон имел дополнительное ограничение на тип второго аргумента Iterator::value_type const & (это может быть реализовано с SFINAE), тогда find не скомпилируется с той же ошибкой.

[1] Обратите внимание на выбор порядка в объявлении: type const *, а не const type *.Они одинаковы для компилятора (и опытного глаза), , но , всегда добавляя const вправо, что упрощает определение того, что определяется как const и определить, что const T & с T == type * в аргументе contains равно , а не const type *&.

[2] Точно так же, как вы не можете связать type * const & с type const *, вы не можете сделать эквивалент с указателями, type * const * нельзя преобразовать из type const *, , но , если constдобавляется как к указателю, так и к типу pointee, тогда преобразование в порядке, поскольку оно гарантирует, что оно не нарушит правильность const.Это преобразование (которое является безопасным) выполняется в сравнении type * == type const * const, где левая сторона получает два дополнительных const: type const * const.Если неясно, почему это не нарушает const-правильность, оставьте комментарий, и я приведу здесь некоторый код.

1 голос
/ 04 ноября 2011

Явная реализация шаблона выполняется для конкретных типов, и вы можете быть уверены, что поставщик стандартной библиотеки не знал, что вы напишите TYPE.Кроме того, разница в подписях.std::find - это бесплатный шаблон функции, что-то вроде:

template <typename I, typename T>
find(I first, I last, T value)

Итак, когда вы вызываете его, компилятор генерирует find(std::vector<TYPE*>::iterator, std::vector<TYPE*>::iterator, const TYPE*).Поскольку find выполняет только сравнения, и вы можете сравнивать const T* и T* без проблем, все хорошо и пушисто.

QVector<TYPE*>::contains, с другой стороны, является функцией-членом,в шаблоне класса.Таким образом, подпись содержит тип, используемый для создания экземпляра шаблона:

contains(TYPE*)

И в этом заключается проблема, потому что вы пытаетесь вызвать его с помощью const TYPE* - и преобразование const T* в T* недопустимо.

Также: find возвращает итератор, а не логическое значение.Ваше условие должно быть if (std::find(...) != that_vector.end()).

Для прямого ответа «Почему QVector :: содержит ожидание указателя на неконстантный ТИП в качестве его параметра»: потому что вы сказали это с аргументом шаблона.

...