C ++ 17: использование std :: option для оценки, содержит ли enum значение - PullRequest
0 голосов
/ 03 октября 2018

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

#include <optional>

enum class test_enum : int {
    VALUE_0 = 0,
    VALUE_1 = 1
};

// Template function to perform check
template<typename T>
constexpr std::optional<T> from_int(int value)
{
    static_assert(false, __FUNCTION__ " not implemented for this type; see build output");
    return std::optional<T>();
}

// Specialization for test_enum
template<>
constexpr std::optional<test_enum> from_int(int value)
{
    switch (value) {
        case static_cast<int>(test_enum::VALUE_0) :
            return test_enum::VALUE_0;
        case static_cast<int>(test_enum::VALUE_1):
            return test_enum::VALUE_1;
        default:
            return std::optional<test_enum>();
    }
}

int main(int argc, char* argv[])
{
    static_assert(from_int<test_enum>(1));

    return 0;
}

При использовании Visual Studio 2017 (версия 15.8.6),код успешно компилируется без ошибок в выводе.Тем не менее, в окне ошибки отображаются

E0028: expression must have a constant value" at line 30. (the first line of main)

и

"std::_Optional_construct_base<test_enum>::_Optional_construct_base(std::in_place_t, _Types &&..._Args) [with _Types=<test_enum>]" (declared implicitly) is not defined)".

Любые подсказки, почему это так?Я могу игнорировать E0028, но я бы предпочел не делать этого, если это возможно.

РЕДАКТИРОВАТЬ: Удаление static_assert из from_int не меняет ошибку.

Ответы [ 3 ]

0 голосов
/ 03 октября 2018

Данный код компилируется без предупреждений или ошибок, используя cl v19.15.26726 (Visual Studio версия 15.9.0-pre.1.0)

0 голосов
/ 03 октября 2018

Гораздо лучше использовать диспетчеризацию тегов, чем специализацию шаблонов в 99/100 случаях.

#include <optional>

enum class test_enum : int {
    VALUE_0 = 0,
    VALUE_1 = 1
};

template<class T> struct tag_t {};

namespace from_int_details {
  template<class T>
  std::optional<T> from_int_impl( tag_t<T>, int value ) = delete;
}
template<class T>
std::optional<T> from_int( int value ) {
  using namespace from_int_details;
  return from_int_impl( tag_t<T>{}, value );
}

// Overload for test_enum, same namespace as test_enum *or* in from_int_details:
constexpr std::optional<test_enum> from_int_impl(tag_t<test_enum>, int value)
{
    switch (value) {
        case static_cast<int>(test_enum::VALUE_0) :
            return test_enum::VALUE_0;
        case static_cast<int>(test_enum::VALUE_1):
            return test_enum::VALUE_1;
        default:
            return std::optional<test_enum>();
    }
}

int main()
{
    static_assert(from_int<test_enum>(1));
}

здесь люди расширяют from_int, записывая constexpr optional<the_enum_type> from_int_impl( tag_t<the_enum_type>, int ) в пространство имен the_enum_type (поэтому его можно найти через ADL) или для перечислений, где это невозможно (например, перечисления в std), в пространстве имен from_int_details.Или пространство имен tag_t.

Вот этот код, компилируемый в MSVC 2017 , версия компилятора 19.10.

0 голосов
/ 03 октября 2018

Похоже, что стандарт определяет такой код как некорректный, диагностика не требуется.Взгляните на следующие утверждения:

[Достоверность шаблона может быть проверена перед любой реализацией.[Примечание: Зная, какие имена являются именами типов, можно таким образом проверять синтаксис каждого шаблона.- примечание к концу] Программа не сформирована, диагностика не требуется, если:

<...>

(8.4) гипотетическая реализация шаблона сразу после его определения будет некорректнойсформированный из-за конструкции, которая не зависит от параметра шаблона ...] 1

Чтобы сделать его правильно сформированным, не используйте static_assert(false).Вместо этого используйте следующий прием (компилируется с GCC 7 и CLang 7):

#include <optional>

enum class test_enum : int {
    VALUE_0 = 0,
    VALUE_1 = 1
};

template<typename T> 
constexpr bool false_t = false;

// Template function to perform check
template<typename T>
constexpr std::optional<T> from_int(int value)
{
    static_assert(false_t<T>, "Not implemented for this type; see build output");
    return std::optional<T>();
}

// Specialization for test_enum
template<>
constexpr std::optional<test_enum> from_int<test_enum>(int value)
{
    switch (value) {
        case static_cast<int>(test_enum::VALUE_0) :
            return test_enum::VALUE_0;
        case static_cast<int>(test_enum::VALUE_1):
            return test_enum::VALUE_1;
        default:
            return std::optional<test_enum>();
    }
}

int main()
{
    static_assert(from_int<test_enum>(1));
}
...