1

事前定義されたタイプのセットをサポートし、使用可能なタイプのどれが現在アクティブであるかを示す列挙を提供する、かなり単純なバリアント クラスがあります。このようなもの:

class variant
{ 
  enum class type { integer, real, string, etc };
  type active_type() const;
  /* ... */ 
};

サポートされている型がテンプレート パラメーターとして提供されるテンプレートにクラスを作成したいと思います。

template <typename... T>
class variant
{ 
  const std::type_info& active_type() const; // logical, but can't switch on it
  /* ... */ 
};

エラーをキャッチするために私が依存している重要な機能はswitch、アクティブな型を使用できることです。コンパイラは、可能なケースのいずれかが見逃された場合に警告を発します。これは、上記の設計を使用して (または を使用してboost::variant) 行うことはできません。

私の質問は、パラメーター パック内の引数の数と同じ数の列挙型を持つ列挙型を自動的に生成する方法はありますか?

列挙型の実際の名前/値は、型を正しい列挙型にマップするために使用される constexpr 関数の背後に隠される可能性があるため、重要ではありません。次のような最終的な使用法を想像できます。

template <typename... T>
class variant
{
  enum class type { T... }; // magic here

  // specializations provided to map T into type (for use in case labels)
  template <typename T>
  static constexpr type type_enum();

  type active_type() const;
  /* ... */
};

typedef variant<int, float, std::string> myvar;
myvar var;
switch (var.active_type())
{
case myvar::type_enum<int>(): // static constexpr function
  ...
  break;
case myvar::type_enum<float>():
  ...
  break;
} // warning: enumeration for string not handled in switch
4

2 に答える 2

5

この質問は 2 年前のものですが、答えがなく、他に解決策が見つからなかったため、問題を解決した方法を次に示します。

最初の明らかな問題は、パラメーターの数が一致する列挙型です(コンパイラーがスイッチチェックを行うため)。私が見る唯一の方法は、一連のテンプレートの特殊化です。

template <typename Tag, size_t size> struct Enum;
template <typename Tag> struct Enum<Tag, 0> {
    enum class Type { };
};
template <typename Tag> struct Enum<Tag, 1> {
    enum class Type {
        VALUE0
    };
};
template <typename Tag> struct Enum<Tag, 2> {
    enum class Type {
        VALUE0, VALUE1
    };
};
template <typename Tag> struct Enum<Tag, 3> {
    enum class Type {
        VALUE0, VALUE1, VALUE2
    };
};
template <typename Tag> struct Enum<Tag, 4> {
    enum class Type {
        VALUE0, VALUE1, VALUE2, VALUE3
    };
};

はい、手動で入力する必要がありますが、大きな問題ではありません。通常、タイプの数は制限されており、必要に応じて新しい専門化を追加できることを念頭に置いてください (つまり、アプローチは静的に安全です)。Tagパラメータは、同じ数の値を持つ列挙型を区別するために使用されます。

次に、型を列挙するための再帰的なテンプレート マジックが必要です。

template <typename EnumType, typename Type, typename... Types> struct TypeInfo;
template <typename EnumType, typename Type, typename... Types> struct TypeInfo<EnumType, Type, Type, Types...> {
    static const EnumType value = EnumType::VALUE0;
};
template <typename EnumType, typename Type, typename FirstType, typename... Types> struct TypeInfo<EnumType, Type, FirstType, Types...> {
    static const EnumType value = static_cast<EnumType>(static_cast<typename std::underlying_type<EnumType>::type>(TypeInfo<EnumType, Type, Types...>::value) + 1);
};

最後に、すべてのピースをまとめるためのクラス:

template <typename Tag, typename... Ts> class TypedEnum {
    private:
        struct InternalTag;

    public:
        static const size_t NUM_TYPES = sizeof...(Ts);
        typedef typename Enum<InternalTag, NUM_TYPES>::Type Type;

        template <typename T> static constexpr decltype(TypeInfo<Type, T, Ts...>::value) getValue() { // SFINAE guard
            return TypeInfo<Type, T, Ts...>::value;
        }
};

出来上がり!切り替えることができます!(clangでテスト済み)

struct Tag0;
struct Tag1;
typedef TypedEnum<Tag0, int, float, char> Enum0;
typedef TypedEnum<Tag0, bool, float, char> Enum1; // Incompatible with Enum0!

Enum0::Type tpy = Enum0::getValue<char>(); // 2
switch(tpy) {
    case Enum0::getValue<int>():
        break;
    case Enum0::getValue<float>():
        break;
    case Enum0::getValue<char>():
        break;
}

機能の概要:

  • 列挙型にない型を静的に拒否する
  • switch ケースの欠落について静的に警告する
  • パック内の異なるタグやパラメーターの列挙型に互換性がない

欠けている機能:

  • 重複のあるパラメーター パックを拒否するには、テンプレート マジックが必要です。
于 2015-09-28T14:21:35.240 に答える