На самом деле это довольно интересный вопрос о правильности констант и некоторых неинтуитивных ошибках, но компилятор прав, отвергая код.
Почему не работает в 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-правильность, оставьте комментарий, и я приведу здесь некоторый код.