Does C++ have comparison operator that specify a r

2019-03-10 20:14发布

I need to write a condition that checks if an enum variable in range of values, like it can be done in E language:

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

Is there a better way in C++ than ask 4 times if EnumVariable==a or EnumVariable==b etc.?

6条回答
甜甜的少女心
2楼-- · 2019-03-10 20:16

this is often/usually accomplished using bit fields in an enum (clearly, up to the number of bits of an integral type (usually 32 bits), that is the default type at the base of an enum):

#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;

}

that shortly can be seen just as:

if ( x1 & (a|g|t|i) ) {
  ...
}
查看更多
我命由我不由天
3楼-- · 2019-03-10 20:20

There is not the same, compact syntax, but you can use std::find with an std::initializer_list. For example,

#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;
}

This can easily be wrapper up in a helper function that takes the enum value, the list, and returns a bool. For example:

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

}

usage:

int main()
{
  EnumVariable ev = a;
  std::cout << std::boolalpha;
  std::cout << is_in(ev, {a, g, t, i}) << std::endl;
}
查看更多
时光不老,我们不散
4楼-- · 2019-03-10 20:20

Yet-another approach is to use a template to specify the list to match in, which for your enum case will presumably be known at compile time (otherwise even the switch approach is inapplicable). This allows a notation like:

if (in<a, b, c, d, e, f>(x))
    ...

With optimisation enabled, this should be inlined and yield something equivalent to:

a == x || b == x || ...

Implemention below (also ideone). I've not played with variadic templates much, so suggestions welcome. In particular, how to make it type-safe so given enum E { E1 }; enum F { F1 };, in<E1>(F1) won't compile (without limiting it to a specific enum type).

#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';
}

Output:

0
1
0
1
查看更多
做自己的国王
5楼-- · 2019-03-10 20:20

It's absolutely not idiomatic, and probably to avoid, but the closest you can have (syntactically speaking) is to use named operators. Named operators actually correspond to an abuse of operator overloading.

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;
}

Here is a working example on Coliru.


If you use C++14 and polymorphic lambdas, you can make the above example more generic:

auto in = make_named_operator(
[](int i, auto const& x) {
    return std::find(std::begin(x), std::end(x), i) != std::end(x);
});
查看更多
迷人小祖宗
6楼-- · 2019-03-10 20:34

In C++11 a brief way to express this is by using the brace-init syntax to construct a set, and calling std::set::count to test if the element is present:

if (std::set<EnumVariable>{a, g, t, i}.count(myVariable))
  ...

A compilable example:

#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;
}

Note about efficiency: since the question asked about a "comparison operator", the answer is written with the goal of providing a fairly readable and brief single expression that does not require a separate utility function. Since it builds a temporary std::set, it is not nearly as efficient as a switch statement, which is compiled into tests or jump tables by all relevant compilers.

查看更多
仙女界的扛把子
7楼-- · 2019-03-10 20:40

It appeared, that several people liked my comment and asked to post it as an answer, so:

You can actually use switch for integral types without break-s between some values.

For example, your code could look like:

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
};

This would be very efficient, but would work for integral types only, and not for std::string, for example. For other types, you may try to use some of the other answers' suggestions.

It's a bit long for writing, but very, very efficient and does what you need.

查看更多
登录 后发表回答