Reputation: 5399
I am looking for an easy, fast and descriptive way in C++ to check if a value is contained in a fixed set of other values. Like in Python, where one can write
if some_function() in (2, 3, 5, 7, 11):
do_something()
Some obvious options are:
switch
/case
: If the values in question are integers, then one can write something like this:
switch (some_function()) {
case 2: case 3: case 5: case 7: case 11: do_something();
}
Unfortunately, this only works for integers, and I daresay it's not very pretty.
Use a local variable to keep the temporary result:
const auto x = some_function();
if (x == 2 || x == 3 || x == 5 || x == 7 || x == 11) do_something();
I would like to avoid the named temporary variable. Furthermore, this is tedious to write and error-prone.
Use std::set
: This can be written (at least in C++20) as:
if (std::set({ 2, 3, 5, 7, 11 }).contains(some_function())) do_something();
That's kinda nice, but I fear it has some heavy STL overhead.
Are there other, cheaper methods? Maybe some variadic template solution?
Upvotes: 4
Views: 1031
Reputation: 5399
Some data on the fast
part of the question:
I compiled the three obvious solutions and the one by @cigien with Visual Studio 2019 (C++ Compiler 19.27.29122) with full optimizations (/Ox /std:c++latest
) and looked at the assembler output. Here is what I found:
This solution turns into a sweet BT
instruction (x64) or a jump table (x86), both only some 10 lines of assembler.
Again a BT
instruction (x64) or the obvious 5 comparisons in a row (x86), again about 10 lines.
Speedwise, this is the horror: 120 lines with 9 calls to other functions, all in all 1500 (!) lines. Plus, it requires exceptions and C++20.
@cigien's solution: the same as 2. for both x64 and x86.
Bottom line: the template version is not only short and expressive, but also small and fast.
Upvotes: 2
Reputation: 415
if it's about style all this stuff has nothing to do with c/c++ anyway.
if it's about performance
#include <iostream>
#include <algorithm>
#include <execution>
int main(int argc , char *argv[])
{
int b[] = {1 ,4 ,7 ,8};
int c = 4;
bool e = std::any_of(std::execution::par, b, &b[0] + sizeof(b)/sizeof(int), [=](int a){ return a == c; });
if (e) std::cout << "cool " << std::endl;
}
c++ -O3 --std=c++17 a.cpp -o t -ltbb
Upvotes: 0
Reputation: 60440
Yes, you can indeed write a variadic template function, combined with a fold-expression, like this:
namespace my
{
template<typename T, typename ... Vals>
bool any_of(T t, Vals ...vals)
{
return (... || (t == vals));
}
}
and then use it like this:
if (my::any_of(some_function(), 2, 3, 5, 7, 11))
{
do_something();
}
Note that I've put any_of
in a namespace so as to avoid any confusion with std::any_of
which is a completely different function.
Upvotes: 8