Непрерывное перечисление C ++ 11 - PullRequest
17 голосов
/ 06 февраля 2020

Есть ли способ проверить в C ++ 11, является ли перечисление непрерывным ?

Допустимо указывать перечислимые значения, которые не являются. Есть ли такая особенность, как черта типа в C ++ 14, C ++ 17 или, может быть, C ++ 20, чтобы проверить, является ли enum непрерывным? Это будет использоваться в static_assert.

Ниже приведен небольшой пример:

enum class Types_Discontinuous {
  A = 10,
  B = 1,
  C = 100
};

enum class Types_Continuous {
  A = 0,
  B = 1,
  C = 2
};

static_assert(SOME_TEST<Types_Discontinuous>::value, "Enum should be continuous"); // Fails
static_assert(SOME_TEST<Types_Continuous>::value, "Enum should be continuous");    // Passes

Ответы [ 4 ]

7 голосов
/ 06 февраля 2020

Для ряда enum s вы, вероятно, можете взломать этот путь, используя библиотеку Magi c Enum . Например:

#include "magic_enum.hpp"

template <typename Enum>
constexpr bool is_continuous(Enum = Enum{}) {
    // make sure we're actually testing an enum
    if constexpr (!std::is_enum_v<Enum>)
        return false;
    else {
        // get a sorted list of values in the enum
        const auto values = magic_enum::enum_values<Enum>();
        if (std::size(values) == 0)
            return true;

        // for every value, either it's the same as the last one or it's one larger
        auto prev = values[0];
        for (auto x : values) {
            auto next = static_cast<Enum>(magic_enum::enum_integer(prev) + 1);
            if (x != prev && x != next)
                return false;
            else
                prev = x;
        }
        return true;
    }
}

Обратите внимание, что это действительно, как следует из названия библиотеки, "magi c" - библиотека функционирует в ряде хаков, определенных компилятором c. Как таковой, он на самом деле не соответствует вашему требованию «чистого C ++», но, вероятно, настолько хорош, насколько мы сможем получить, пока у нас не появятся средства отражения в языке.

7 голосов
/ 06 февраля 2020

Это невозможно в чистом C ++, потому что нет способа перечислить значения перечисления или определить количество значений, а также минимальное и максимальное значения. Но вы можете попытаться использовать помощь вашего компилятора, чтобы реализовать что-то близкое к тому, что вы хотите. Например, в g cc можно применить ошибку компиляции, если оператор switch не обрабатывает все значения перечисления:

enum class my_enum {
    A = 0,
    B = 1,
    C = 2
};

#pragma GCC diagnostic push
#if __GNUC__ < 5
#pragma GCC diagnostic error "-Wswitch"
#else
#pragma GCC diagnostic error "-Wswitch-enum"
#endif

constexpr bool is_my_enum_continuous(my_enum t = my_enum())
{
    // Check that we know all enum values. Effectively works as a static assert.
    switch (t)
    {
    // Intentionally no default case.
    // The compiler will give an error if not all enum values are listed below.
    case my_enum::A:
    case my_enum::B:
    case my_enum::C:
        break;
    }

    // Check that the enum is continuous
    auto [min, max] = std::minmax({my_enum::A, my_enum::B, my_enum::C});
    return static_cast< int >(min) == 0 && static_cast< int >(max) == 2;
}

#pragma GCC diagnostic pop

Очевидно, это специализировано для данного перечисления, но определение таких функций может быть автоматизировано с помощью препроцессора.

1 голос
/ 06 февраля 2020

Я бы хотел увидеть ответ на этот вопрос. Мне это тоже нужно.

К сожалению, я не думаю, что это возможно с использованием существующих утилит. Если вы хотите реализовать для этого черту типа, вам нужна поддержка вашего компилятора, поэтому написание шаблона для него не представляется возможным.

Я уже расширил перечисление с помощью специального тега c чтобы указать, что он является смежным и сразу дает вам размер: конструктор класса enum c ++, как передать конкретное значение c?

В качестве альтернативы, вы можете написать свою собственную черту:

 template<T> struct IsContiguous : std::false_type {};

Это должно быть специализированным, когда вы определяете непрерывное перечисление, где вы хотите использовать это. К сожалению, это требует некоторого обслуживания и внимания, если перечисление меняется.

1 голос
/ 06 февраля 2020

Все перечисления являются непрерывными. 0 всегда разрешено; самое высокое допустимое значение - это самый высокий перечислитель, округленный до следующего 1<<N -1 (все биты один), и все значения между ними также разрешены. ([dcl.enum] 9.7.1 / 5). Если определены отрицательные перечислители, то самое низкое допустимое значение аналогично определяется округлением в меньшую сторону перечислителя.

Перечисленные в enum перечислители являются константными выражениями со значением в диапазоне и правильным типом, но вы можете определить дополнительные константы вне enum, которые имеют такие же свойства:

constexpr enum class Types_Discontinuous = static_cast<Types_Discontinuous>(2)

...