Использование std::not_fn
для отрицания предиката
В качестве ядра алгоритма этого вопроса (как было элегантно описано путем объединения std::find_if
и std::none_of
в принятый ответ ) с коротким замыканием при сбое - это сканирование контейнера на наличие унарного предиката и, если оно выполнено, продолжение сканирования остальной части контейнера на предмет отрицания предиката, я также упомяну отрицатель std::not_fn
введено в C ++ 17, заменяя менее полезные конструкции std::not1
и std::not2
.
Мы можем использовать std::not_fn
для реализации той же логики предиката, что и принятый ответ (std::find_if
условно сопровождается std::none_of
), но с несколько иной семантикой, заменив последний шаг (std::none_of
) на std::all_of
сверх отрицания унарного предиката, использованного на первом шаге (std::find_if
). E.g.:
// C++17
#include <algorithm> // std::find_if
#include <functional> // std::not_fn
#include <ios> // std::boolalpha
#include <iostream>
#include <iterator> // std::next
#include <vector>
template <class InputIt, class UnaryPredicate>
constexpr bool one_of(InputIt first, InputIt last, UnaryPredicate p) {
auto it = std::find_if(first, last, p);
return (it != last) && std::all_of(std::next(it), last, std::not_fn(p));
}
int main() {
const std::vector<int> v{1, 3, 5, 6, 7};
std::cout << std::boolalpha << "Exactly one even number : "
<< one_of(v.begin(), v.end(), [](const int n) {
return n % 2 == 0;
}); // Exactly one even number : true
}
Подход пакета параметров для контейнеров статического размера
Поскольку я уже ограничил этот ответ C ++ 14 (и более поздними версиями), я включу альтернативный подход для контейнеров статического размера (здесь применяется только для std::array
), используя объединенное std::index_sequence
с расширением пакета параметров:
#include <array>
#include <ios> // std::boolalpha
#include <iostream>
#include <utility> // std::(make_)index_sequence
namespace detail {
template <typename Array, typename UnaryPredicate, std::size_t... I>
bool one_of_impl(const Array& arr, const UnaryPredicate& p,
std::index_sequence<I...>) {
bool found = false;
auto keep_searching = [&](const int n){
const bool p_res = found != p(n);
found = found || p_res;
return !found || p_res;
};
return (keep_searching(arr[I]) && ...) && found;
}
} // namespace detail
template <typename T, typename UnaryPredicate, std::size_t N,
typename Indices = std::make_index_sequence<N>>
auto one_of(const std::array<T, N>& arr,
const UnaryPredicate& p) {
return detail::one_of_impl(arr, p, Indices{});
}
int main() {
const std::array<int, 5> a{1, 3, 5, 6, 7};
std::cout << std::boolalpha << "Exactly one even number : "
<< one_of(a, [](const int n) {
return n % 2 == 0;
}); // Exactly one even number : true
}
Это также приведет к короткому замыканию при раннем отказе («найдено более одного»), но будет содержать несколько более простых логических сравнений, чем в подходе выше.