Безопасно ли в С++ использовать std::numeric_limits‹double›::max() в качестве специального флага?

Дано

std::vector<double> a;
std::vector<int> ind;

где ind – это 1отсортировано по возрастанию.

Я хочу сделать эквивалент следующего:

for (auto it=ind.rbegin();it!=ind.rend();it++) a.erase(a.begin() + *it);

Я придумал это:

for (auto it=ind.begin();it!=ind.end();it++) 
   a[*it] = std::numeric_limits<double>::max();
std::erase(std::remove(a.begin(),a.end(),std::numeric_limits<double>::max()),a.end());

Это очень быстро, но использование std::numeric_limits::max() в качестве флага в этом контексте кажется неправильным.

Конечно, чувства не должны играть слишком большую роль в уравнении... четкое сравнение двойников в std::remove безопасно, и на практике предел никогда не будет возникать в этом векторе в рабочем приложении, так что все должно быть в порядке, нет?

Есть мысли по этому поводу?


1) Ref комментарий OP. – Альф


person Jens    schedule 16.02.2015    source источник
comment
Невозможно исключить вектор, уже содержащий это значение, поскольку вы сами показываете, что вектор может содержать это значение. На этот вопрос невозможно ответить, если вы четко не задокументируете, какие предположения верны, а какие нет. Другое предположение заключается в том, что элементы ind расположены в порядке возрастания и не имеют дубликатов, но на самом деле вы не говорите об этом в своем вопросе.   -  person    schedule 16.02.2015
comment
Есть и другие значения, которые вы можете выбрать в качестве флагов: положительный и отрицательный inf, а также NaN.   -  person user1095108    schedule 16.02.2015
comment
Вместо этого может быть лучше использовать std::numeric_limits<double>::infinity() или std::numeric_limits<double>::quiet_NaN(), если известно, что ни одно из значений в массивах не будет этим значением.   -  person tmlen    schedule 16.02.2015
comment
Да, в моем случае элементы ind отсортированы, следовательно, альтернатива/эквивалент обратной итерации.   -  person Jens    schedule 16.02.2015
comment
Хорошие моменты в отношении бесконечности или тишины_NaN. Они никогда не появятся в массиве, если только что-то еще серьезно не так...   -  person Jens    schedule 16.02.2015
comment
Я бы не стал использовать NAN, так как его сложно протестировать — ни один из стандартных алгоритмов с ним не работает.   -  person Mark Ransom    schedule 16.02.2015
comment
Можно ли случайно получить бесконечность, разделив на ноль, или это вызовет исключение?   -  person Mark Ransom    schedule 16.02.2015
comment
Я думаю, что если double a=1./0;, то std::isinf(a) оценивается как true. Насчет исключения не уверен. В моем случае я мог бы заранее проверить инфы, так как они не должны встречаться в моем контексте. Но, похоже, было бы лучше реализовать мою собственную идиому стирания-удаления, которая может использовать записи в ind, а не значения, которые идентифицируют записи для удаления.   -  person Jens    schedule 16.02.2015
comment
Что не так с вашим первым фрагментом?   -  person sbabbi    schedule 17.02.2015
comment
Возможно, ничего, но, как указывает первый ответ, есть вероятность, что std::numeric_limits<double>::max() уже является числом в векторе, и, следовательно, операция завершится ошибкой. В идеале я хотел бы зарезервировать специальный двойник, который я мог бы использовать в качестве флага, например, используя std::numeric_limits<double>::max() в качестве флага, но в то же время уменьшая максимальный двойник для всех остальных до второго по величине возможного двойное значение.   -  person Jens    schedule 17.02.2015
comment
@shabbi: прямой подход в первом фрагменте — O(a.size()*ind.size()).   -  person Cheers and hth. - Alf    schedule 17.02.2015


Ответы (1)


Давайте посмотрим на ваш «базовый» код, который, как вы говорите, хотите сделать «эквивалент»:

std::vector<double> a;
std::vector<int> ind;

for (auto it = ind.rbegin(); it != ind.rend(); it++)
    a.erase(a.begin() + *it);

Из этого мы получаем, что ind — это вектор индексов в a, который следует удалить, и что они отсортированы по возрастанию. Эти индексы должны быть удалены из a. Я предполагаю, что ваша цель - сделать это эффективно с точки зрения пространства и времени.

Если вы думаете об этом с точки зрения минимального количества необходимых операций, вам нужно сдвинуть многие/большинство/все элементы в a, чтобы стереть элементы, указанные ind. Вы не хотите erase() несколько раз, потому что это означает сдвиг некоторых элементов более одного раза. Одно оптимальное решение (в общем случае, не предъявляющее особых требований к значениям в a) выглядит так:

size_t slow = 0; // where we are currently copying "to"
std::vector<int> inditer = ind.begin();
for (size_t fast = 0; fast != a.size(); ++fast) {
    if (inditer != ind.end() && *inditer == fast) {
        // "remove" this element by ignoring it
        ++inditer;
    } else {
        a[slow] = a[fast];
        ++slow;
    }
}
a.resize(slow);

Теперь вы, вероятно, могли бы переформулировать это, используя алгоритмы STL и пользовательский предикат (функтор), который запоминает свою «текущую» позицию в ind, но это не будет намного меньше кода, и его может быть труднее понять.

person John Zwinck    schedule 17.02.2015
comment
Большое спасибо - да, я думаю, что решением будет просто реализовать что-то вроде этого. Несколько удивительно, однако, что нет аналога std::remove для этой конкретной ситуации. Я бы предположил, что это распространенный случай. - person Jens; 17.02.2015
comment
@Jens: Я не могу припомнить, чтобы хотел сделать что-то подобное, но даже если бы это было более непосредственно поддержано STL, есть пара проблем: (1) ваш ind имеет дело с целыми числами, а не с итераторами, и (2) ind должны быть отсортированы. Алгоритмы STL, как правило, более простые/общие/широко применимые. Думайте о STL как о наборе примеров того, что вы можете делать с итераторами, а не как о наборе инструментов с фиксированным содержимым. - person John Zwinck; 17.02.2015