auto e = expired_connections[i] - i;
Если вам нужно вычесть смещение, то вы удалили итератор перед - но это означает, что все последующие итераторы были признаны недействительными! Вы не можете использовать их больше, не вызывая неопределенное поведение!
Теперь давайте предположим, что будет действительным. Тогда у вас все еще есть другая проблема:
outgoing_connections.push_back(makeConnection());
outgoing_connections.push_back(makeConnection());
// now for any reason, second connection expires before first one!!!
expired_connections.push_back(outgoing_connections.begin() + 1);
expired_connections.push_back(outgoing_connections.begin());
Вы бы сейчас попытались стереть outgoing_connections.begin() - 1
! Опять неопределенное поведение.
Вам нужно будет отсортировать итераторы, а затем удалить в правильном порядке, так что сначала вы удалите самый последний итератор. Это, по крайней мере, позволит избежать неопределенного поведения, но довольно неэффективно: каждый раз, когда вы стираете один элемент из вектора, вы перемещаете все последующие элементы вперед. Многократные перемещения по возможно большим диапазонам ...
Лучшее, что вы можете сделать, это сбросить этот expired_connections
вектор и вместо этого отметить, что сами соединения истекли:
std::vector<std::tuple<
std::future<std::string>, // Connection future
int, // socket
std::chrono::time_point<high_resolution_clock>, // timeout timer
std::shared_ptr<bool> // needs to exist
bool // expired
> outgoing_connections;
(Честно говоря, отдельный класс / struct с именованными членами будет лучшим выбором!)
Тогда вы можете использовать erase-remove-idiom в сочетании с std::remove_if
для сброса закрытых соединений:
outgoing_connections.erase
(
std::remove_if
(
outgoing_connections.begin(), outgoing_connections.end(),
[](auto& connection)
{
// minimalistic variant, if need be add output back again...
return connection.isExpired && !close(connection.fd);
};
), // returns an iterator to new end
outgoing_connections.end()
);
Преимущество заключается в том, что std::remove_if
(а также std::remove
) начинают удаление впереди вектора и перемещают все элементы, которые должны оставаться впереди - возможно, пропуская несколько позиций одновременно, поэтому каждый элемент переместился только один раз, ниже того, как эта функция могла бы быть реализована:
template <typename Iterator, typename Condition>
Iterator remove_if(Iterator begin, Iterator end, Condition condition)
{
Iterator pos = begin;
for(; begin != end; ++begin)
{
if(!condition)
{
// element shall remain!
if(begin != pos)
*pos++ = *begin;
}
}
return pos;
}
Вы видите: Каждый элемент касается только один раз ... Пока, однако, элементы перемещаются только (или копируется, если не доступно перемещение), но лишние элементы в конце не удаляются. Вот почему вам все еще нужен звонок на erase
.