3

C++ で列挙型を記述するときのパターンにたどり着きました。次のようになります。

class Player
{
public:
    class State
    {
    public:
        typedef enum
        {
            Stopped, 
            Playing, 
            Paused
        }PossibleValues;  

        static const int Count() {return Paused+1;};
        static const PossibleValues Default() {return Stopped;};
    };

    //...
}

これにより、外部の名前空間の汚染など、列挙型に関する通常の問題のいくつかが解決されます。ただし、まだ気に入らない点があります。Count() は手動で行われます。私が知っている方法は 2 つしかありません。これは Last+1 から計算されます。またはハードコードされたプレーンな書き込み。

質問: プリプロセッサ マクロを使用するなど、カウントを自動的に取得して Count() メソッドの後に配置する方法はありますか? 注意: enum 内に Count という最後の偽の要素を入れて、それを汚染したくありません!

前もって感謝します!

更新 1:

より高度な列挙型の提案について、標準 C++11 での N4428 列挙型リフレクションの実装 (部分的)に関する興味深い議論があります。

更新 2:

興味深いドキュメントN4451- MetaEnums および MetaEnumClasses に関するセクション 3.16、3.17、A.7、A.8 の静的リフレクション (rev. 3) 。

更新 3:

https://bytes.com/topic/c/answers/127908-numeric_limits-specialization#post444962を見た後、列挙型クラスを使用して別の興味深いパターンにたどり着きました。enum クラスの列挙子リストが連続して integerである場合、その最大値と最小値を定義することで、値がそれに属するかどうかを確認できます。

Count()でメソッドを使用する目的がPlayer::State値が列挙に含まれているかどうかを確認することであった場合、その目的は numeric_limits アプローチでも達成されており、列挙子リストがゼロ値の項目で始まる必要がないため、さらに優れています。 !

enum class Drink
{
    Water,
    Beer,
    Wine,
    Juice,
};


#pragma push_macro("min")
#undef min

#pragma push_macro("max")
#undef max

namespace std
{
    template <> class numeric_limits < Drink >
    {
    public:
        static const/*expr*/ bool is_specialized = true;

        static const/*expr*/ Drink min() /*noexcept*/ { return Drink::Water; }
        static const/*expr*/ Drink max() /*noexcept*/ { return Drink::Juice; }

        static const/*expr*/ Drink lowest() /*noexcept*/ { return Drink::Water; }

        static const/*expr*/ Drink default() /*noexcept*/ { return Drink::Beer; }
    };
}

#pragma pop_macro("min")
#pragma pop_macro("max")

使用例:

アプリケーションからの変数:

Drink m_drink;

コンストラクターで次のように初期化されます。

m_drink = numeric_limits<Drink>::default();

フォームの初期化時に、次のことができます。

pComboDrink->SetCurSel(static_cast<int>(theApp.m_drink));

その上で、ユーザーが行った変更にインターフェイスを適応させるために、スコープ付きの列挙型クラス値を使用して切り替えを行うことができます。

switch (static_cast<Drink>(pComboDrink->GetCurSel()))
{
case Drink::Water:
case Drink::Juice:
    pAlcohoolDegreesControl->Hide();
break;

case Drink::Beer:
case Drink::Wine:
    pAlcohoolDegreesControl->Show();
break;

default:
    break;
}

そして、ダイアログの確認手順 ( OnOK) で、それぞれのアプリ変数に保存する前に、値が境界外であるかどうかを確認できます。

int ix= pComboDrink->GetCurSel();

if (ix == -1)
    return FALSE;

#pragma push_macro("min")
#undef min

#pragma push_macro("max")
#undef max

if (ix < static_cast<int> (std::numeric_limits<Drink>::min()) ||  ix > static_cast<int> (std::numeric_limits<Drink>::max()) )
    return FALSE;

#pragma pop_macro("min")
#pragma pop_macro("max")

theApp.m_drink= static_cast<Drink>(ix);

ノート:

  1. キーワードconstexpr(私はコメントしましたが、その/*expr*/ままにしておきますconst) とnoexceptは、私が使用しているコンパイラ (Visual C++ 2013) が現在のバージョンでまだサポートしていないため、コメントされています。
  2. おそらく、最小マクロと最大マクロを一時的に未定義にするロジックは必要ありません。
  3. default()が「数値制限」の範囲に収まらないことはわかっています。しかし、それを置くのに便利な場所に思えました。default文脈によってはキーワードであるという単語と一致します。
4

5 に答える 5

3

いいえ、ありません。これが必要な場合は、enumそもそもを使用するべきではありません。

あなたの特定のケースでは、あなたが呼び出したいユースケースは何Countですか?

于 2012-10-19T10:29:47.573 に答える
3

私の知る限り、の要素の合計量を取得するための自動コンパイラサポートキーワードはありませんenum。OTOH これは通常は意味がありません: 値が結果値を持つ必要がない限り、同じ値を持つ複数の値を持つことができます (つまり、自動番号付けに頼るのではなく、手動で値を割り当てることができます)。

enum一般的な方法の 1 つは、次の方法でを宣言することです。

  typedef enum
    {
        Stopped, 
        Playing, 
        Paused,

        count

    }PossibleValues;  

このように、countが常に最後に定義されている場合、番号付けが 0 から始まり、その結果であると仮定すると、列挙型要素の数が得られます。

于 2012-10-19T10:30:57.353 に答える
0

PossibleValues の型は列挙型でなければなりませんか? 列挙型のように動作するものが必要な場合は、次のことができます。

#include <iostream>

#include <functional>
#include <set>


template <typename Representation, typename T>
class Iterable_Strong_Enum
{
private:
  struct T_Ptr_Less : public std::binary_function<T const *, T const *, bool>
  {
    bool operator()(T const * x, T const * y) const
    {
      return x->get_representation() < y->get_representation();
    }
  };

public:
  typedef std::set<T const *, T_Ptr_Less> instances_list;
  typedef typename instances_list::const_iterator const_iterator;

  Representation const & get_representation() const { return _value; }

  static Representation const & min() { return (*_instances.begin())->_value; }

  static Representation const & max() { return (*_instances.rbegin())->_value; }

  static T const * corresponding_enum(Representation const & value)
  {
    const_iterator it = std::find_if(_instances.begin(), _instances.end(), [&](T const * e) -> bool
    {
      return e->get_representation() == value;
    });
    if (it != _instances.end())
    {
      return *it;
    }
    else
    {
      return nullptr;
    }
  }

  bool operator==(T const & other) const { return _value == other._value; }
  bool operator!=(T const & other) const { return _value != other._value; }
  bool operator< (T const & other) const { return _value <  other._value; }
  bool operator<=(T const & other) const { return _value <= other._value; }
  bool operator> (T const & other) const { return _value >  other._value; }
  bool operator>=(T const & other) const { return _value >= other._value; }

  static bool is_valid_value(Representation const & value) { return corresponding_enum(value) != nullptr; }

  static typename instances_list::size_type size() { return _instances.size(); }

  static const_iterator begin() { return _instances.begin(); }

  static const_iterator end() { return _instances.end(); }

protected:
  explicit Iterable_Strong_Enum(Representation const & value);

private:
  Representation _value;

  static instances_list _instances;
};

template <typename Representation, typename T>
Iterable_Strong_Enum<Representation, T>::Iterable_Strong_Enum(Representation const & value)
: _value(value)
{
  _instances.insert(static_cast<T const *>(this));
}

class PossibleValues : public Iterable_Strong_Enum<int, PossibleValues>
{
public:
  static const PossibleValues Stopped;
  static const PossibleValues Playing;
  static const PossibleValues Pause;
protected:
private:
  explicit PossibleValues(int value);
};

PossibleValues::PossibleValues(int value) : Iterable_Strong_Enum<int, PossibleValues>(value) { }

// you need to call that explicitly
Iterable_Strong_Enum<int, PossibleValues>::instances_list Iterable_Strong_Enum<int, PossibleValues>::_instances;

const PossibleValues PossibleValues::Stopped(0);
const PossibleValues PossibleValues::Playing(1);
const PossibleValues PossibleValues::Pause(2);

void stackoverflow()
{
  std::cout << "There are " << PossibleValues::size() << " different possible values with representation: " << std::endl;
  for (auto pv = PossibleValues::begin(); pv != PossibleValues::end(); ++pv)
  {
    PossibleValues possible_value = **pv;
    std::cout << possible_value.get_representation() << std::endl;
  }
}

私はその解決策についてちょっと引き裂かれています。一方で、それは非常に一般的であり、他方では、小さな問題に対する大きなハンマーです。

于 2012-10-19T11:38:37.900 に答える