C ++ 17:使用std :: optional评估枚举是否包含值

时间:2018-10-03 18:19:39

标签: c++ enums c++17 constexpr stdoptional

我想在编译时检查各种枚举是否包含给定值,因此我编写了以下内容:

#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,但如果可能,我不希望这样做。

编辑:从from_int中删除static_assert不会更改错误。

3 个答案:

答案 0 :(得分:3)

似乎该标准将这样的代码定义为格式错误,不需要诊断。看一下以下语句:

  

[可以在任何实例化之前检查模板的有效性。   [注:知道哪些名称是类型名称允许每个语法   以这种方式检查模板。 — [尾注]该程序是   格式错误,无需诊断,如果:

     

<...>

     

(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));
}

答案 1 :(得分:3)

在99/100的情况下,使用标签分发比使用模板专业化要好得多。

std::string filename("input.bmp");
bitmap_image image(filename);

人们通过在#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 )(这样可以通过ADL找到它),或者为不可能的枚举(例如the_enum_type名称空间中std中的枚举。或from_int_details的命名空间。

以下是此代码在MSVC 2017(编译器版本19.10)中进行编译。

答案 2 :(得分:0)

有问题的代码使用cl v19.15.26726(Visual Studio版本15.9.0-pre.1.0)编译时没有警告或错误