C++是否有指定一系列值的比较运算符?(比如E语言中的'in')?

Hal*_*ona 26 c++ comparison-operators

我需要写一个条件来检查值范围内的枚举变量是否可以用E语言完成:

enum EnumVariable {a, b, d, g, f, t, k, i};
if (EnumVariable in [ a, g, t, i]) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

在C++中有没有比问4次更好的方法if EnumVariable==a or EnumVariable==b

Kir*_*rov 28

似乎有几个人喜欢我的评论,并要求将其作为答案发布,所以:

实际上,你可以使用switch积分类型,而break有些值之间-s.

例如,您的代码可能如下所示:

enum EnumVariable {a, b, d, g, f, t, k, i};
switch( EnumVariable )
{
case a:
case g:
case t:
case i:
    // do something
    break;
// handler other values
};
Run Code Online (Sandbox Code Playgroud)

这将是非常有效的,但仅适用于整数类型,而不适用std::string于例如.对于其他类型,您可以尝试使用其他一些答案的建议.

写作有点长,但非常非常有效,能满足您的需求.

  • @MatthewPigram将字符串转换为char数组没什么用,因为`switch`会将char数组与指针值进行比较,因此具有相同内容的不同字符串会有不同的比较. (2认同)

use*_*342 21

在C++ 11中,表达这一点的一种简单方法是使用brace-init语法构造一个set,并调用std::set::count以测试该元素是否存在:

if (std::set<EnumVariable>{a, g, t, i}.count(myVariable))
  ...
Run Code Online (Sandbox Code Playgroud)

一个可编辑的例子:

#include <set>

enum EnumVariable {a, b, d, g, f, t, k, i};

int main()
{
  EnumVariable x = t;
  if (std::set<EnumVariable>{a, g, t, i}.count(x)) {
    return 0;
  }
  return 1;
}
Run Code Online (Sandbox Code Playgroud)

关于效率的注意事项:由于问题是关于"比较运算符",所以写答案的目的是提供一个相当可读且简短的表达式,不需要单独的效用函数.由于它构建了一个临时文件std::set,因此它不如switch语句高效,语句由所有相关编译器编译成测试或跳转表.

  • @juanchopanza:如果`set`是一次创建并重新使用那么公平,但根据这个答案(此时)将每个元素放入临时`set`中的机会不大 - 每个元素都需要一个动态内存分配和解除分配. (6认同)
  • 要用于这样的条件,我宁愿使用`make_set`函数来推导模板参数.它不那么冗长. (4认同)
  • 它*可能*对于大型集合来说更快.查找是订单日志(N).人们必须测量等等. (2认同)
  • 问题不仅在于分配,而且构建集合需要时间n*log(n),因此您真的希望在此集合上进行多次测试以使其值得. (2认同)
  • @juanchopanza源代码中字面上列出的一组枚举不太可能出现大集,就像这里的情况一样.即使这样,编译器也可以为大型`switch`语句生成非常有效的跳转表(事实上,许多编译器都知道这样做). (2认同)

jua*_*nza 17

这里是不一样的,紧凑的语法,但你可以使用std::findstd::initializer_list.例如,

#include <algorithm>
#include <iostream>

enum EnumVariable {a, b, d, g, f, t, k, i};

int main()
{
  EnumVariable e = k;
  auto vals = { a, g, t, i }; // std::initializer_list<EnumVariable>
  std::cout << std::boolalpha;
  std::cout << (std::find(vals.begin(), vals.end(), e) != vals.end()) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

这可以很容易地包含在一个辅助函数中,它接受枚举值,列表并返回一个bool.例如:

template <typename T>
bool is_in(T elem, std::initializer_list<T> range)
{
 for (T i : range)
    if (i == elem) return true;
 return false;

}
Run Code Online (Sandbox Code Playgroud)

用法:

int main()
{
  EnumVariable ev = a;
  std::cout << std::boolalpha;
  std::cout << is_in(ev, {a, g, t, i}) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)


Sig*_*ndo 16

这通常/通常使用枚举中的位字段来实现(显然,最多为整数类型的位数(通常为32位),这是枚举基数的默认类型):

#include <iostream>

enum EnumVariable {
  a=0x0001, 
  b=0x0002, 
  d=0x0004, 
  g=0x0008, 
  f=0x0010, 
  t=0x0020, 
  k=0x0040, 
  i=0x0080
};

inline bool CheckMatch(EnumVariable var, EnumVariable mask) {
  return (var & mask) != 0;
}

using namespace std;

int main(int argc, char **argv) {
  EnumVariable mask = EnumVariable(a|g|t|i);
  EnumVariable x1 = b;
  EnumVariable x2 = g;

  cout << "x1: " << (CheckMatch(x1, mask) ? "match" : "no-match") << endl;
  cout << "x2: " << (CheckMatch(x2, mask) ? "match" : "no-match") << endl;

}
Run Code Online (Sandbox Code Playgroud)

很快就可以看到:

if ( x1 & (a|g|t|i) ) {
  ...
}
Run Code Online (Sandbox Code Playgroud)


Ton*_*roy 10

然而,另一种方法是使用模板来指定要匹配的列表,对于您的enum情况,可能在编译时已知(否则即使该switch方法也不适用).这允许使用如下符号:

if (in<a, b, c, d, e, f>(x))
    ...
Run Code Online (Sandbox Code Playgroud)

启用优化后,应该内联并产生等效于:

a == x || b == x || ...
Run Code Online (Sandbox Code Playgroud)

下面的实现(也是ideone).我没有玩过多变量模板,所以欢迎提出建议.特别是,如何使其类型安全如此给定enum E { E1 }; enum F { F1 };,in<E1>(F1)将不会编译(不限于特定enum类型).

#include <iostream>
using namespace std;

template <int First, int... Numbers>
inline bool in_impl(int n)
{
    return n == First || in_impl<Numbers...>(n);
}

template <>
inline bool in_impl<int(-1)>(int n)
{
    return false;
}

template <int... Numbers>
inline bool in(int n) 
{
    return in_impl<Numbers..., int(-1)>(n);
}

enum E { E1, E2, E3, E4 };

int main()
{
    std::cout << in<3, 5, 7, 9>(4) << '\n';
    std::cout << in<3, 5, 7, 9>(5) << '\n';
    std::cout << in<E2, E4>(E1) << '\n';
    std::cout << in<E2, E4>(E2) << '\n';
}
Run Code Online (Sandbox Code Playgroud)

输出:

0
1
0
1
Run Code Online (Sandbox Code Playgroud)


Mor*_*enn 6

它绝对不是惯用的,可能要避免,但你可以拥有的最接近的语法(语法上)是使用命名运算符.命名运算符实际上对应于滥用运算符重载.

enum EnumVariable {a, b, d, g, f, t, k, i};

auto in = make_named_operator(
[](int i, std::initializer_list<EnumVariable> const& x) {
    return std::find(std::begin(x), std::end(x), i) != std::end(x);
});

auto vals = { a, g, t, i };
if (g <in> vals)
{
    std::cout << "It works!" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

是Coliru的一个工作示例.


如果您使用C++ 14和多态lambda,您可以使上面的示例更通用:

auto in = make_named_operator(
[](int i, auto const& x) {
    return std::find(std::begin(x), std::end(x), i) != std::end(x);
});
Run Code Online (Sandbox Code Playgroud)