42

のデフォルトのコンストラクタを指定する方法はありますenum classか?

を使用してenum class、ライブラリ内の特定のデータ型に許容される一連の値を指定しています。この場合、これは Raspberry Pi の GPIO ピン ID 番号です。次のようになります。

enum class PinID : int {N4 = 4, N17 = 17, /* ...etc... */ }

たとえば、単に an を使用する代わりにこれを行うポイントは、intコードが安全であることを確認することです: 私はできるstatic_assert(またはそうでなければコンパイル時に保証する - 使用される実際のメソッドは私にとって重要ではない) 誰かがしていないようなことスペルミス (4 の代わりに 5 を渡すなど) があり、型の不一致などの自動エラーメッセージが表示されます。

問題enum classは、デフォルトのコンストラクターがあり、C との互換性のために(動作が同じであるため)、と同等のものにenum初期化されることです。この場合、値はありません。これは、ユーザーが次のような宣言/定義を行うことを意味します。enum class00

PinID pid = PinID();

明示的に定義されていない列挙子を取得しており (コードを見ても「存在」していないように見えます)、実行時エラーが発生する可能性があります。これはまた、明示的に定義された列挙子の値に対する ing のような手法は、エラー/デフォルトのケースがなければ不可能であることを意味します。これswitchは、私が避けたいことですthrowboost::optional分析。

デフォルトのコンストラクターを無駄に定義しようとしました。私は (必死に) の名前を共有する関数を定義しようとしましたenum classが、これは (驚くべきことではありませんが) 奇妙なコンパイラ エラーを引き起こしました。すべての列挙子をそれぞれの にマッピングして、 enum classtoをキャストする機能を保持したいので、N4 = 0 のように単に「定義」することは受け入れられません。これは単純さと正気のためです。intN##

私の質問は 2 つあると思いenum classます。そうでない場合、他にどのような可能性を好むでしょうか? 私が欲しいのは次のようなものです:

  1. デフォルトで構築可能です
  2. デフォルト構成を任意の有効な値にすることができます
  3. enum classesによって提供される「指定された有限集合」の値を提供します
  4. 少なくとも同じくらい型安全ですenum class
  5. (できれば) ランタイム ポリモーフィズムを含まない

デフォルトの構成可能性が必要な理由は、値boost::lexical_cast間の変換に伴う構文上のオーバーヘッドと、オペレーティング システム (この場合は sysfs) に出力するenum class実際の関連付けられたs を削減するために使用する予定があるためです。デフォルトの構築可能性が必要です。stringboost::lexical_cast

私の推論の誤りは大歓迎です --enum classこの場合、es が間違った仕事のための正しいオブジェクトであると疑い始めています。質問されれば、明確化が提供されます。お時間をいただきありがとうございます。

4

3 に答える 3

22

enum classorで定義された型enum structは、クラスではなくスコープ付き列挙体であり、既定のコンストラクターを定義することはできません。C++11 標準では、PinID pid = PinID();ステートメントがゼロ初期化を与えると定義されています。PinIDとして定義された場所enum class。また、一般に、列挙型が列挙子定数以外の値を保持できるようにします。

PinID() がゼロの初期化を与えることを理解するには、標準セクション3.9.9、8.5.5、8.5.7、および8.5.10を一緒に読む必要があります。

8.5.10 -An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized

8.5.7 - To value-initialize an object of type T means:...otherwise, the object is zero-initialized.

8.5.5 -To zero-initialize an object or reference of type T means: — if T is a scalar type (3.9), the object is set to the value 0 (zero), taken as an integral constant expression, converted to T;

3.9.9 - 列挙型は、スカラー型として知られる型のセットの一部であると述べています。

考えられる解決策:

ポイント 1 から 5 を満たすために、次の行に沿ってクラスを作成できます。

class PinID
{
private:
    PinID(int val)
    : m_value(val)
    {}

    int m_value;

public:
    static const PinID N4;
    static const PinID N17;
    /* ...etc... */ 

    PinID() 
    : m_value(N4.getValue())
    {}

    PinID(const PinID &id)
    : m_value(id.getValue())
    {}

    PinID &operator = (const PinID &rhs)
    {
        m_value = rhs.getValue();
        return *this;
    }

    int getValue() const
    {
        return m_value;
    }

    // Attempts to create from int and throw on failure.
    static PinID createFromInt(int i);

    friend std::istream& operator>>(std::istream &is, PinID &v)
    {
        int candidateVal(0);
        is >> candidateVal;
        v = PinID::createFromInt(candidateVal);
        return is;
    }
};

const PinID PinID::N4 = PinID(4);
/* ...etc... */

これにより、無効な値を取得するために特定の努力をしなければならない何かが得られる可能性があります。デフォルトのコンストラクターとストリーム演算子は、lexical_cast で動作できるようにする必要があります。

クラスを作成する価値があるか、値が使用されているときに無効な値を処理するだけの価値があるかどうかは、PinID の作成後の操作がどれほど重要であるかに依存するようです。

于 2013-07-13T15:59:23.090 に答える
5

Anenum classは厳密に型指定されたenum; ではありませんclass。C++11 はclass、従来の C++ コードとの互換性を損なう新しいキーワードの導入を避けるために、既存のキーワードを再利用しただけです。

あなたの質問に関しては、キャストが適切な候補を含むことをコンパイル時に保証する方法はありません。検討:

int x;
std::cin >> x;
auto p = static_cast<PinID>(x);

これは完全に合法であり、コンソール ユーザーが正しいことを行ったことを静的に保証する方法はありません。

代わりに、実行時に値が有効であることを確認する必要があります。自動化された方法でこれを回避するために、私の同僚の 1 人が、enum列挙値を含むファイルを指定して、これらのチェックとその他の役立つルーチンをビルドするジェネレーターを作成しました。自分に合った解決策を見つける必要があります。

于 2013-07-13T14:39:44.773 に答える
4

この質問は古く、すでに受け入れられている回答があることは知っていますが、C ++の新しい機能のいくつかを使用して、このような状況で役立つ可能性のある手法を次に示します

このクラスの変数は または のいずれnon staticstaticで宣言できます。現在のコンパイラのサポートで許可されているいくつかの方法で実行できます。


非静的:

#include <iostream>
#include <array>

template<unsigned... IDs>
class PinIDs {
private:
    const std::array<unsigned, sizeof...(IDs)> ids { IDs... };
public:
    PinIDs() = default;
    const unsigned& operator[]( unsigned idx ) const {
        if ( idx < 0 || idx > ids.size() - 1 ) {
            return -1;
        }
        return ids[idx];
    }
};

静的: - これを記述する方法は 3 つあります: (最初の 1 つ - C++11 または 14 以降) 最後の 2 つ (c++17)。

C++11 の部分で私を引用しないでください。可変個引数テンプレートまたはパラメーター パックが最初に導入された時期はよくわかりません。

template<unsigned... IDs>
class PinIDs{
private:        
    static const std::array<unsigned, sizeof...(IDs)> ids;
public:    
    PinIDs() = default;    
    const unsigned& operator[]( unsigned idx ) const {
        if ( idx < 0 || idx > ids.size() - 1 ) {
            return -1;
        }
        return ids[idx];
    }
};

template<unsigned... IDs>
const std::array<unsigned, sizeof...(IDs)> PinIDs<IDs...>::ids { IDs... };

template<unsigned... IDs>
class PinIDs{
private:
    static constexpr std::array<unsigned, sizeof...(IDs)> ids { IDs... }; 
public:   
    PinIDs() = default;    
    const unsigned& operator[]( unsigned idx ) const {
        if ( idx < 0 || idx > ids.size() - 1 ) {
            return -1;
        }
        return ids[idx];
    }
};

template<unsigned... IDs>
class PinIDs{
private:
    static inline const std::array<unsigned, sizeof...(IDs)> ids { IDs... };
public:    
    PinIDs() = default;    
    const unsigned& operator[]( unsigned idx ) const {
        if ( idx < 0 || idx > ids.size() - 1 ) {
            return -1;
        }
        return ids[idx];
    }
};

上記の非静的または静的のすべての例は、以下の同じユース ケースで動作し、正しい結果を提供します。

int main() {
    PinIDs<4, 17, 19> myId;

    std::cout << myId[0] << " ";
    std::cout << myId[1] << " ";
    std::cout << myId[2] << " ";

    std::cout << "\nPress any key and enter to quit." << std::endl;
    char c;
    std::cin >> c;

    return 0;
}

出力

4 17 19
Press any key and enter to quit.

可変個引数リストを使用するこのタイプのクラス テンプレートでは、デフォルト以外のコンストラクターを使用する必要はありません。operator[]がそのサイズの境界を超えないように、配列に境界チェックを追加しました。エラーをスローすることはできましたが、unsignedタイプを使用すると、単に無効な値として -1 が返されました。

このタイプでは、単一または一連の値を持つテンプレート パラメータ リストを介してこの種のオブジェクトをインスタンス化する必要があるため、デフォルトはありません。必要に応じて、デフォルト タイプspecialize this classの単一パラメータを使用できます。0このタイプのオブジェクトをインスタンス化するとき。宣言から変更できないため、最終的なものです。これは const オブジェクトであり、デフォルトで構築可能です。

于 2017-12-20T08:07:38.743 に答える