Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check whether an element is in std::initializer_list

I want to be able to write in C++ something similar to the following Python code:

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

to test whether an element is contained in a set of hard-coded values, defined in-place. Like this:

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

Here is the possible implementation of the in function:

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

My question is: do I really have to write this function by myself? Are there any default implementations over there? Maybe in boost? I checked boost::contains, but it works only with strings.

like image 865
Mikhail Avatar asked Dec 15 '14 20:12

Mikhail


3 Answers

If you have access to c++20 you can use set's contains which returns a bool allowing you to do:

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

Live Example


Otherwise, with just c++11 you can still use set's count which only returns 1 or 0 allowing you to do something like:

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

Live Example


Keep in mind that magic numbers can be confusing for your audience (and cause 5 seasons of Lost.)
I'd recommend declaring your numbers as a const initializer_list<int> and giving them a meaningful name:

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

if(cend(finalCandidates) != find(cbegin(finalCandidates), cend(finalCandidates), x))
like image 123
Jonathan Mee Avatar answered Nov 11 '22 17:11

Jonathan Mee


boost::algorithm::contains doesn't only work on strings, it works on any range, i.e. a sequence that can yield a begin and end iterator. To find a single value use it as follows:

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

You can perform your search using the standard library only, but doing so is quite a bit more verbose. A couple of options are:

  1. using a lambda

    if(std::any_of(l.begin(), l.end(), 
                   [](int i){ return i == 2; })) { ... }
    
  2. using std::bind

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

Live demo

Note that std::equal_to<>() is a C++14-only option. For a C++11 compiler, use std::equal_to<int>().

like image 22
Praetorian Avatar answered Nov 11 '22 18:11

Praetorian


Indeed the STL does not have a simple std::contains() function. Recently, there was a discussion on reddit about this topic.

Unfortunately, what came out of this is that it is considered harmful to have std::contains(), since it encourages people to write slow algorithms. Think for instance of

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

This code example essentially searches for the correct position twice: Once in contains, and once to find the insert location.

In my opinion, it would still be very helpful to have std::contains(), but so far no one was convinced yet to write a proposal.

So either use boost (as suggested by other in this thread), or write your own function which you essentially already did :-)

like image 1
dhaumann Avatar answered Nov 11 '22 16:11

dhaumann