Проверьте, находится ли элемент в std:: initializer_list

Я хочу иметь возможность писать на С++ нечто похожее на следующий код Python:

if x in [1, 2, 3, 5] ...

чтобы проверить, содержится ли элемент в наборе жестко заданных значений, определенных на месте. Вот так:

if (in(x, {1, 2, 3, 5})) ...

Вот возможная реализация функции in:

template<class T>
bool in(const T& x, std::initializer_list<T> c)
{
  return std::find(c.begin(), c.end(), x) != c.end();
}

Мой вопрос: действительно ли мне нужно написать эту функцию самостоятельно? Существуют ли какие-либо реализации по умолчанию там? Может быть, в boost? Я проверил boost::contains, но работает только со строками.

Ответ 1

Если у вас есть доступ к вы можете использовать set contains, который возвращает bool, позволяющее сделать:

if(set{ 4, 8, 15, 16, 23, 42 }.contains(x))

Live Example


Если у вас нет , вы все равно можете использовать set count который возвращает только 1 или 0, что позволяет вам сделать что-то вроде:

if(set<int>{ 4, 8, 15, 16, 23, 42 }.count(x) > 0U)

Имейте в виду, что магические числа могут сбить с толку вашу аудиторию (и вызвать 5 сезонов Lost.)
Я бы рекомендовал объявить ваши номера как const initializer_list<int> и дать им значимое имя:

const auto finalCandidates{ 4, 8, 15, 16, 23, 42 };

if(cend(finalCandidates) != find(cbegin(finalCandidates), cend(finalCandidates), x))

Ответ 2

boost::algorithm::contains работает не только на строках, но и работает на любом диапазоне, то есть в последовательности, которая может дать начало и конец итератора. Чтобы найти одно значение, используйте его следующим образом:

auto l = {1,2,3,4};
auto l1 = {2};      // thing you want to find
if(boost::algorithm::contains(l, l1)) { ... }

Вы можете выполнять поиск, используя только стандартную библиотеку, но это довольно немного подробней. Пара вариантов:

  • используя lambda

    if(std::any_of(l.begin(), l.end(), 
                   [](int i){ return i == 2; })) { ... }
    
  • используя std::bind

    using std::placeholders::_1;
    if(std::any_of(l.begin(), l.end(), 
                   std::bind(std::equal_to<>(), 2, _1)) { ... }
    

Живая демонстрация

Обратите внимание, что std::equal_to<>() - это опция только для С++ 14. Для компилятора С++ 11 используйте std::equal_to<int>().

Ответ 3

В действительности STL не имеет простой функции std::contains(). Недавно в этой теме было обсуждение reddit.

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

if (!std::contains(my_set.begin(), my_set.end(), entry)) {
    my_set.insert(insert);
}

Этот пример кода в основном ищет правильное положение дважды: один раз внутри содержит и один раз, чтобы найти место вставки.

По-моему, было бы очень полезно иметь std::contains(), но пока никто не был убежден написать предложение.

Так что либо используйте boost (как было предложено другим в этом потоке), либо напишите свою собственную функцию, которую вы по существу уже сделали: -)