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



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

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

标准似乎将这样的代码定义为格式不正确,无需诊断。请看以下语句:

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

<...>

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

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

在这里,人们通过在the_enum_type命名空间中编写constexpr optional<the_enum_type> from_int_impl( tag_t<the_enum_type>, int )来扩展from_int(因此可以通过 ADL 找到(,或者在from_int_details命名空间中为不可能这样做的枚举(如std中的枚举(编写。 或者tag_t的命名空间。

下面是在 MSVC 2017 编译器版本 19.10 中编译的此代码。

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

最新更新