Фрагмент кода
std::map<int, int> m = { { 1, 2 }, { 3, 4 } };
boost::range::remove_erase_if (
m,
[](const auto& it)
{
return it.first == 1;
});
выдает ошибку
12>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.29.30133
\include\xmemory(1986,1): error C2679: binary '=': no operator found which takes a right-hand operand of type 'std::pair<const int,int>' (or there is no acceptable conversion)
12>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.29.30133\include\utility(269,11): message : could be 'std::pair<const int,int> &std::pair<const int,int>::operator =(volatile const std::pair<const int,int> &)'
12>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.29.30133\include\xmemory(1986,1): message : while trying to match the argument list '(std::pair<const int,int>, std::pair<const int,int>)'
Я ожидал, что remove_erase_if удалит с карты первую пару {1, 2}.
remove_erase_if ведет себя так, как ожидалось, когда я пробую его с вектором вместо карты:
std::vector<int> v = { 1, 2, 3, 4 };
boost::range::remove_erase_if (
v,
[](const auto& it)
{
return it == 1;
});
Здесь v содержит {2, 3, 4} после выполнения. Что мне нужно настроить, чтобы использовать remove_erase_if с картой?
it
— очень запутанное имя переменной. Разве вы не можете использовать std::erase_if
из C++20? По крайней мере, это работает. std::erase_if (m, [](const auto& pair) { return pair.first == 1; });
. Если вы хотите только erase
в зависимости от ключа, вы можете просто использовать m.erase(1);
Мне кажется, что если это сработает на vector
, то может никогда не сработать на map
. Чтобы удалить элементы из вектора, вы должны переместить другие элементы на новые позиции. То же самое невозможно на карте, вы не можете переназначать узлы.
Я видел, что большинство реализаций до сих пор использовали it
в таком случае, но я вижу, как это сбивает с толку, поскольку они не печатают iterator
. Но это не объясняет ошибку. К сожалению, я не могу использовать std::erase_if
, так как наш проект сейчас работает на C++17. Иначе я бы с удовольствием это сделал. У меня также были проблемы с std::erase_if
, и я могу обойти это с помощью классического цикла for
. Но я хотел бы понять, почему код не работает.
@Yksisarvinen std::erase_if
работает, просто перебирая все элементы на карте и вызывая m.erase()
те, у которых есть предикат true
. Это очень дорогой способ удаления с использованием значения ключа.
«Я не могу использовать std::erase_if, так как наш проект сейчас работает на C++20». Но, как я уже сказал, std::erase_if
— это C++20. Почему вы хотите использовать erase_if
на ключе? Придется просматривать всю карту. Почему бы просто не сделать m.erase(1);
?
@Ted Lyngmo: Извините, опечатка. Я имел в виду С++17.
Хорошо, еще. Использование std::erase_if
для удаления значений в соответствии с ключом не является правильным. Это очень дорого по сравнению с m.erase(Key);
@TedLyngmo Я имел в виду boost::range::remove_erase_if
. То, как вы удаляете элементы из std::vector
и std::map
, принципиально отличается, если только remove_erase_if
не использует метапрограммирование для различения типов контейнеров, оно не может работать как с std::vector
, так и с std::map
.
Пример здесь является просто заполнителем для простоты. В моем реальном коде условие удаления более сложное. Я предполагаю, что в этом случае вам нужно перебрать все записи карты и проверить, верно ли условие для каждой.
@Yksisarvinen Да boost::remove_erase_if
просто делает m.erase(std::remove_if (m.begin(), m.end(), [](const auto& it) { return it.first == 1; }), m.end());
, что не очень хорошо работает на std::map
. Денникс: Я предлагаю вам просто скопировать пример реализации std::erase_if (std::map). Это работает.
В вашем случае boost::remove_erase_if
будет делать то же самое, что и
m.erase(std::remove_if (m.begin(), m.end(), [](const auto& it) { return it.first == 1; }), m.end());
что плохо работает на std::map
s.
Если вы не можете использовать C++20 std::erase_if (std::map), сделайте свой собственный. При последующем обновлении до C++20 вы можете заменить пространство имен dnx
на std
в своем коде, который использует dnx::erase_if
.
namespace dnx {
template <class Key, class T, class Compare, class Alloc, class Pred>
typename std::map<Key, T, Compare, Alloc>::size_type erase_if (
std::map<Key, T, Compare, Alloc>& c, Pred pred) {
auto old_size = c.size();
for (auto i = c.begin(), last = c.end(); i != last;) {
if (pred(*i)) {
i = c.erase(i);
} else {
++i;
}
}
return old_size - c.size();
}
} // namespace dnx
int main() {
std::map<int, int> m = {{1, 2}, {3, 4}};
// now works:
dnx::erase_if (m, [](const auto& it) { return it.first == 1; });
}
Но разве я не получаю доступ к этому парному ключу с помощью it.first?