Я не очень хорошо знаком с C++ и не понимаю, как работает компаратор.
В приведенном ниже коде показано, как найти позицию, в которую я хочу вставить новый интервал:
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
vector<vector<int>> intervals; // = ...
vector<int> newInterval; // = ...
// ...
auto comp = [](const vector<int>& a, const vector<int>& b) { return a[0] < b[0]; };
auto it = upper_bound(intervals.begin(), intervals.end(), newInterval, comp);
}
Когда я попытаюсь удалить const
, компиляция выдаст ошибку.
Зачем мне добавлять const
для этого специального компаратора?
Обратите внимание, что константную функцию-член можно вызывать как для неконстантного, так и для константного объекта. Но неконстантную функцию-член можно вызвать только для константного объекта. Хорошая книга по C++
Примечание: если вы изучаете C++, вам следует разучиться using namespace std;
. См.: В чем проблема с «использованием пространства имен std;»?.
std::upper_bound
определено так, что const T& value
принимается в качестве третьего параметра. И он передает это value
компаратору. Таким образом, компаратор должен принять объект const
хотя бы в качестве первого параметра.
operator()
пользовательского компаратора должен быть const
функцией-членом. Но это не то const
, о котором спрашивается этот вопрос. (operator()
лямбды по умолчанию равен const
, если лямбда не отмечена mutable
).
Всегда публикуйте полную ошибку вместе с вашим вопросом. Кроме того, это не минимально воспроизводимый пример
@IgorTandetnik Еще один обман: Почему оператор вызова лямбды неявно является константой?
const
в первом параметре достаточно для компиляции кода (не то чтобы это была хорошая идея). Это потому, что std::upper_bound
всегда вызывает компаратор как comp(value, *it)
, где value
— его третий параметр, а it
— некоторый итератор в диапазоне; и value
берется по константной ссылке.
@ user12002570 ОП, кажется, спрашивает, почему параметры лямбды должны быть const
; почему auto comp = [](vector<int>& a, vector<int>& b) { return a[0] < b[0]; };
не работает. По крайней мере, я так понял вопрос. Они говорят: «Я пытался удалить const
», и это единственные случаи const
, явно присутствующие в показанном коде.
@IgorTandetnik интересно, что в документации указано comp
, что «Хотя подпись не обязательно должна иметь const &, функция не должна изменять переданные ей объекты и должна иметь возможность принимать все значения типа (возможно, const) Type1 и Тип2 независимо от категории значения». Интересно, как этого можно добиться без const&
?
@Yksisarvinen, спасибо, я понял это (см. Мой ответ).
Является ли мотивом этого вопроса тот факт, что во многих местах, если const X
приемлемо (например, в качестве аргумента функции), то X
тоже приемлемо?
Зачем мне добавлять «const» в пользовательский компаратор? Потому что уже слишком поздно менять язык, чтобы сделать const
неявное значение по умолчанию и требовать mutable
указывать неконстантный ссылочный параметр.
Конечно! Спасибо @wohlstad за четкий ответ :)
@Юань рад помочь. Кстати, на случай, если вы не в курсе: вы также можете проголосовать за ответ, голосование и принятие различны. См.: Что мне делать, если кто-то отвечает на мой вопрос?.
Из документации std::upper_bound относительно компаратора (comp
):
Сигнатура предикатной функции должна быть эквивалентна следующий:
bool pred(const Type1 &a, const Type2 &b);
Хотя подпись не обязательно должна содержать const &, функция должна не изменять передаваемые ему объекты и должен иметь возможность принимать все значения типа (возможно, const).
(таким образом, Type1 & не разрешен, как и Type1, если только для Type1 перемещение не эквивалентно копированию (начиная с C++11))
(выделено мной)
Поскольку компаратор должен иметь возможность принимать значения const
(и не должен их изменять), тривиальным решением является использование const &
. Другая альтернатива — принимать объекты по значению, что актуально только для простых типов, таких как int
.
Примечание:
Как прокомментировал @IgorTandetnik на практике только первый объект должен быть const, во всех трёх основных компиляторах (gcc, clang, MSVC) - см. демо. Но это верно только в том случае, если используются неконстантные итераторы (как в опубликованном вами коде). Если вы используете константные итераторы (cbegin()
, cend()
), для компиляции оба объекта в компараторах должны быть const
.
В любом случае хорошей практикой является наличие const &
для обоих объектов в компараторе.
Причина, по которой второй параметр может быть неconst-ref, заключается в том, что передаются неконстантные итераторы. Если вы используете функции с префиксом c
или делаете v
константой, например std::upper_bound(v.cbegin(), v.cend(), 0, comp)
, код не скомпилируется.
@Evg - хорошая мысль. Добавил уточнение в примечание.
Поскольку компаратор не должен изменять то, что он сравнивает, это сделает сравнение невозможным.