20

このようなよく知られた値がたくさんあるとしましょう(ただし、これconst char *は単なる例であり、より複雑になる可能性があります)。

const char *A = "A", *B = "B", *C = "C", *D = "D", *E = "E", *F = "F", *G = "G";

ここで、ある式の結果がそれらのサブセットに含まれている場合に、特定の方法で動作したいとします。

if (some_complicated_expression_with_ugly_return_type == A ||
    some_complicated_expression_with_ugly_return_type == C ||
    some_complicated_expression_with_ugly_return_type == E ||
    some_complicated_expression_with_ugly_return_type == G)
{
    ...
}

私はこの種のことを頻繁にタイプしているので、その省略形が欲しいと思います。

言語がPythonの場合、私は簡単に次のように言うことができます。

if some_complicated_expression_with_ugly_return_type in [A, C, E, G]:
    ...

これをC++03で同様に表現するための、よく知られたポータブルな方法はありますか?

戻り型自体が醜い(ラムダ式の戻り型とほぼ同じくらい醜い)ので、ローカル変数に格納したくないことに注意してください。

ただし、戻り型は定数の型と一致する必要はありませ。たとえば、戻り型がである場合、暗黙的にに変換するstd::stringことはできませんconst char *operator ==、比較にはまったく問題ありません。

これまでのところ、私が持っている最善の解決策は、次のようなことを言うことです。

const char *items[] = { A, C, E, G };
if (std::find(items, items + sizeof(items) / sizeof(*items),
              some_complicated_expression_with_ugly_return_type)
    != items + sizeof(items) / sizeof(*items))
{
    ...
}

しかし、それはかなり醜いです。非PODでも機能するより良い方法はありますか?

4

8 に答える 8

27

C ++ 11をお持ちの場合:

auto res = some_complicated_expression_with_ugly_return_type;
if (res == A
    || res == C
    || res == E
    || res == G) {
}

そうでない場合でも、テンプレート関数を使用して型宣言を削除できます。

template <class T>
bool matches(T t) {
    return t == A || t == C || t == E || t == G;
}

if (matches(some_complicated_expression_with_ugly_return_type)) {
}
于 2012-09-02T22:05:25.393 に答える
23

現在の最良のソリューションをテンプレートに組み込むことができます。

template<class A, class B, size_t n>
inline bool is_in(const A &a, B (&bs)[n]) {
  return std::find(bs, bs + n, a) != bs + n;
}

あなたが好きに使うことができる

X items[] = { A, C, E, G };
if (is_in(some_complicated_expression_with_ugly_return_type, items))
  …
于 2012-09-02T22:14:01.947 に答える
12

あなたは使用することができますswitch

switch (some_complicated_expression_with_ugly_return_type) {
  case A: case C: case E: case G:
    // do something
  default:
    // no-op
}

これは整数型と列挙型でのみ機能します。注意してください。

より複雑なタイプの場合は、C ++ 11を使用できauto、C ++ 03の場合は、ブーストBOOST_AUTOを使用できます。

auto tmp = some_complicated_expression_with_ugly_return_type;
// or
BOOST_AUTO(tmp, some_complicated_expression_with_ugly_return_type);

if (tmp == A || tmp == C || tmp == E || tmp == G) {
  // ...
}
于 2012-09-02T22:04:25.280 に答える
9

(編集:ダミータイプを使用した元のトリックが機能しなかったことが判明しました。テストでの幸運な事故に惑わされました。もう一度試してみましょう...)

いくつかのヘルパーテンプレートを使用して、この種の状況の一般的なソリューションを作成できます。

template <typename T1> class Matcher {
public:
    explicit Matcher(T1 t1): val(t1), flag(false) {}
    template <typename T2> Matcher& operator()(T2 t2)
        { flag |= val == t2; return *this; }
    operator bool() const { return flag; }
private:
    T1 val;
    bool flag;
};
template <typename T1> Matcher<T1> match(T1 t1) { return Matcher<T1>(t1); }

// example...
string s = whatever;
if (match(s)("foo")("bar")("zap")) { do_something(); }

必要な数の引数と照合できます。

于 2012-09-03T03:11:59.240 に答える
4

タイプの式

if (some_complicated_expression_with_ugly_return_type == A ||
    some_complicated_expression_with_ugly_return_type == C ||
    some_complicated_expression_with_ugly_return_type == E ||
    some_complicated_expression_with_ugly_return_type == G)
{
    ...
}

コードでは非常に一般的です(まあ、事前に計算された式はとにかくです)。読みやすさのためにできる最善のことは、式を事前に計算してそのままにしておくことだと思います。

ugly_return_type x = some_complicated_expression_with_ugly_return_type;
if (x == A ||
    x == C ||
    x == E ||
    x == G)
{
    ...
}

開発者はこのタイプの構文に慣れています。これにより、他の誰かがあなたのコードを読んでいるときの理解が非常に簡単になります

それはまたあなたが完璧に欲しいものを表現します。このタイプの構文が既存のコードで非常に広く使用されているのには理由があります。他の選択肢は読みやすさが悪いためです。

もちろん、条件を関数でラップすることもできますが、それは再利用可能であり、論理的に意味がある場合に限ります(ポイントIMOを除く)。

于 2012-09-03T08:42:51.213 に答える
1

これは、c++03の可変個引数関数を使用して次のように実行できます。

template <typename T>
bool MatchesOne( T _lhs, int _count, ...)
{
    va_list vl;
    va_start(vl,_count);
    for (int i=0;i<_count;i++)
    {
        int rhs=va_arg(vl,int);
        cout << "rhs = " << rhs << endl;
        if (_lhs == rhs) return true;
    }
    va_end(vl);
    return false;
}

int main(){
    float ff = 3.0;
    if (MatchesOne(ff, 5, 1, 2, 4, 5, 3))
    {
        cout << "Matches" << endl;
    }
    return 0;
}

すべての式のタイプが_lhsと同じタイプになることがわかっている場合は、次のように変更できますint rhs=va_arg(vl,int);T rhs=va_arg(vl,T);

c ++ 11の可変個引数テンプレートを使用して、これをエレガントに行うこともできます。

template<typename T, typename T2>
bool one_equal(const T & _v1, const T2 & _v2)
{
    return _v1 == _v2;
}

template<typename T, typename T2, typename... Args>
bool one_equal(const T & _v1, const T2 & _v2, Args... args)
{
    return _v1 == _v2 || one_equal(_v1, args...);
}

...

if (one_equal(some_complicated_expression, v1, v2, v3, v4))
{

}

さて、最後のハックっぽい解決策です。それは機能しますが、この関数の実装者に多くの反復作業を行わせます。

template <typename T1, typename T2>
bool match_one(T1 _v1, T2 _v2)
{
    return _v1 == _v2;
}

template <typename T1, typename T2, typename T3>
bool match_one(T1 _v1, T2 _v2, T3 _v3)
{
    return _v1 == _v3 || match_one(_v1, _v2);
}

template <typename T1, typename T2, typename T3, typename T4>
bool match_one(T1 _v1, T2 _v2, T3 _v3, T4 _v4)
{
    return _v1 == _v4 || match_one(_v1, _v2, _v3);
}

template <typename T1, typename T2, typename T3, typename T4, typename T5>
bool match_one(T1 _v1, T2 _v2, T3 _v3, T4 _v4, T5 _v5)
{
    return _v1 == _v5 || match_one(_v1, _v2, _v3, _v4);
}
于 2012-09-02T22:14:18.810 に答える
0

切り替えない場合、おそらくこのようなもの、私はそれを使用しませんでしたが、何かが機能するためのドラフトである可能性がありますか?

template <class ReturnType>
bool average(ReturnType expression, int count, ...)
{
  va_list ap;
  va_start(ap, count); //Requires the last fixed parameter (to get the address)
  for(int j=0; j<count; j++)
    if(expression==va_arg(ap, ReturnType))
      return true;
    return false
  va_end(ap);
}
于 2012-09-02T22:16:16.703 に答える
-1

C ++ 11:

template<typename T1, typename T2>
bool equalsOneOf (T1&& value, T2&& candidate) {
   return std::forward<T1>(value) == std::forward<T2>(candidate);
}

template<typename T1, typename T2, typename ...T>
bool equalsOneOf (T1&& value, T2&& firstCandidate, T&&...otherCandidates) {
   return (std::forward<T1>(value) == std::forward<T2>(firstCandidate))
     || equalsOneOf (std::forward<T1> (value), std::forward<T>(otherCandidates)...);
}

if (equalsOneOf (complexExpression, A, D, E)) { ... }

C ++ 03:

template<typename T, typename C>
bool equalsOneOf (const T& value, const C& c) { return value == c; }

template<typename T, typename C1, typename C2>
bool equalsOneOf (const T& value, const C1& c1, const C2& c2) {
    return (value == c2) || equalsOneOf (value, c1);
}

template<typename T, typename C1, typename C2, typename C3>
bool equalsOneOf (const T& value, const C1& c1, const C2& c2, const C3& c3) {
    return (value == c3) || equalsOneOf (value, c1, c2);
}

template<typename T, typename C1, typename C2, typename C3, typename C4>
bool equalsOneOf (const T& value, const C1& c1, const C2& c2, const C3& c3, const C4& c4) {
    return (value == c4) || equalsOneOf (value, c1, c2, c3);
}

template<typename T, typename C1, typename C2, typename C3, typename C4, typename C5>
bool equalsOneOf (const T& value, const C1& c1, const C2& c2, const C3& c3, const C4& c4, const C5& c5) {
    return (value == c5) || equalsOneOf (value, c1, c2, c3, c4);
}

// and so on, as many as you need
于 2012-09-02T22:16:41.913 に答える