2

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

#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 不会改变错误。

4

3 回答 3

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));
}
于 2018-10-03T18:39:43.980 回答
3

在 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编写 a来扩展(因此可以通过 ADL 找到),或者在命名空间中编写不可能的枚举(例如 in中的枚举)。或.constexpr optional<the_enum_type> from_int_impl( tag_t<the_enum_type>, int )the_enum_typestdfrom_int_detailstag_t

这是在MSVC 2017编译器版本 19.10 中编译的代码。

于 2018-10-03T19:48:06.330 回答
0

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

于 2018-10-03T19:18:51.897 回答