C++17:使用 std::optional 来评估枚举是否包含值
C++17: using std::optional to evaluate if enum contains value
我想在编译时检查各种枚举是否包含给定值,所以我编写了以下内容:
#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 Studio2017(版本15.8.6),代码编译成功,输出没有错误。但是,错误 window 显示
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 不会更改错误。
似乎标准将此类代码定义为格式错误,不需要诊断。看看下面的语句:
[The validity of a template may be checked prior to any instantiation.
[ Note: Knowing which names are type names allows the syntax of every
template to be checked in this way. — end note ] The program is
ill-formed, no diagnostic required, if:
<...>
(8.4) a hypothetical
instantiation of a template immediately following its definition would
be ill-formed due to a construct that does not depend on a template
parameter...] 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));
}
有问题的代码使用 cl
v19.15.26726(Visual Studio 版本 15.9.0-pre.1.0)编译时没有警告或错误
在 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 找到它),或者对于不可能这样做的枚举(就像 std
中的枚举),在 from_int_details
命名空间中。或者tag_t
.
的命名空间
这是在 MSVC 2017 中编译的代码,编译器版本 19.10。
我想在编译时检查各种枚举是否包含给定值,所以我编写了以下内容:
#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 Studio2017(版本15.8.6),代码编译成功,输出没有错误。但是,错误 window 显示
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 不会更改错误。
似乎标准将此类代码定义为格式错误,不需要诊断。看看下面的语句:
[The validity of a template may be checked prior to any instantiation. [ Note: Knowing which names are type names allows the syntax of every template to be checked in this way. — end note ] The program is ill-formed, no diagnostic required, if:
<...>
(8.4) a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter...] 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));
}
有问题的代码使用 cl
v19.15.26726(Visual Studio 版本 15.9.0-pre.1.0)编译时没有警告或错误
在 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 找到它),或者对于不可能这样做的枚举(就像 std
中的枚举),在 from_int_details
命名空间中。或者tag_t
.
这是在 MSVC 2017 中编译的代码,编译器版本 19.10。