在C++中检查与多个值的相等性

3

我正在寻找一种在C++中易于快速描述的方法,用于检查一个值是否包含在其他一组固定值中。就像在Python中一样,可以写成

if some_function() in (2, 3, 5, 7, 11):
    do_something()

一些明显的选项包括:

  1. 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.

  2. 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.

  3. 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.

还有其他更便宜的方法吗?也许一些可变参数模板的解决方案?


选项3是合适的。除非在测试中真的成为瓶颈,否则不要因性能问题而将其排除。 - R Sahu
我喜欢你更新的基准测试。然而,那不应该是问题的一部分。相反,将其发布为答案。 - cigien
3个回答

8

是的,您确实可以编写一个变参模板函数,结合fold表达式,就像这样:

namespace my 
{
  template<typename T, typename ... Vals>
  bool any_of(T t, Vals ...vals)
  {
     return (... || (t == vals));
  }
}  

然后像这样使用它:

if (my::any_of(some_function(), 2, 3, 5, 7, 11))
{
    do_something();
}

请注意,我将any_of放在一个命名空间中,以避免与完全不同的函数std::any_of混淆。

我冒昧地重命名了模板,这样它更容易使用,不会与 std::any_of 冲突。 - cxxl
@cxxl 不,那不是一个好主意。除非你引入了命名空间std,否则没有理由与std::any_of发生冲突,而你本来就不应该这样做。相反,把这个函数放到另一个命名空间里,比如my,然后显式地限定它为my::any_of。这是一个更好的选择。 - cigien
1
如果我的上一个评论有意义,那么请继续编辑我的答案以显示该选项。否则,我会自己编辑它。无论哪种方式,我绝对不想因为std中有一个同名函数而将名称从any_of更改为其他名称。 - cigien
@cxxl 已编辑。现在有意义了吗? - cigien
1
现在我们都很高兴。 :) - cxxl
显示剩余2条评论

2

关于问题中的快速部分的一些数据:

我使用Visual Studio 2019(C++编译器19.27.29122)进行了三个明显的解决方案以及@cigien的一个解决方案的全优化编译(/Ox /std:c++latest),并查看了汇编输出。以下是我的发现:

  1. 此解决方案转化为一个甜美的BT指令(x64)或跳转表(x86),都只有大约10行汇编代码。

  2. 再次是一个BT指令(x64)或5个连续的比较(x86),同样大约10行代码。

  3. 就速度而言,这是可怕的:120行代码,9个对其他函数的调用,总共1500(!)行。此外,它需要异常和C++20。

  4. @cigien的解决方案:对于x64和x86都与2相同。

底线:模板版本不仅简短且表达力强,而且小巧快速。


2
好多了,谢谢 :) 关于基准测试的标准免责声明,请确保编译时开启了优化。此外,您可以尝试使用 quick-bench 这个方便的网站来比较一些代码片段的性能。 - cigien
是的,我忘记了 -O3。如果我们讨论的是具有 5 个元素的学校数组,则性能将接近。 - Алексей Неудачин

0

如果是关于风格的话,所有这些东西与C/C++无关。
如果是关于性能的话

#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


这段代码是关于编程的。

2
你为什么要这样传递第三个参数?为什么不只使用“begin”和“end”? - cigien
它将同时加载所有硬件线程,因为b不是一个向量。 - Алексей Неудачин
2
我的意思是,为什么不使用std::any_of(std::execution::par, std::begin(b), std::end(b), ...呢? - cigien
1
我发现这个解决方案很难阅读。此外,它只有在与非常大的值集进行比较时才能发挥其速度优势。 - cxxl
@cigien说,如果我们有C字符串&b[0] + strlen(b)将比std::end(b) - 1更好。 - Алексей Неудачин
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接