c++ - 向作用域枚举添加按位运算和转换为布尔值 - 圣诞探索
问题描述
假设我疯了,并决定创建以下怪物:
#include <type_traits>
#include <iostream>
// Utility proxy type - convertible back to E but also permits bool conversion
// for use in conditions.
//
// e.g.
// Foo f = Foo::Bar & Foo::Baz;
// if (f & Foo::Baz) { /* ... */ }
//
template <typename E, typename = std::enable_if_t<std::is_enum_v<E>, void>>
struct EnumToBoolProxy
{
operator E() const
{
return _val;
}
explicit operator bool()
{
using UT = std::underlying_type_t<E>;
return static_cast<UT>(_val) != 0;
}
private:
const E _val;
EnumToBoolProxy(const E val) : _val(val) {}
friend EnumToBoolProxy operator&(const E, const E);
friend EnumToBoolProxy operator|(const E, const E);
};
enum class Foo
{
Bar = 1, Baz = 2, Boi = 4
};
EnumToBoolProxy<Foo> operator&(const Foo lhs, const Foo rhs)
{
using UT = std::underlying_type_t<Foo>;
return static_cast<Foo>(static_cast<UT>(lhs) & static_cast<UT>(rhs));
}
EnumToBoolProxy<Foo> operator|(const Foo lhs, const Foo rhs)
{
using UT = std::underlying_type_t<Foo>;
return static_cast<Foo>(static_cast<UT>(lhs) | static_cast<UT>(rhs));
}
int main()
{
// Good
if ((Foo::Bar | Foo::Baz) & Foo::Baz)
std::cout << "Yay\n";
// Fine
const bool isFlagSet((Foo::Bar | Foo::Baz) & Foo::Baz);
std::cout << isFlagSet << '\n';
// Meh
auto proxyThing = (Foo::Bar | Foo::Baz) & Foo::Baz;
}
目标是:
- 有一个范围枚举,使得值是
Foo::x
并且是类型Foo
(对称!) - 能够对它们进行一些“按位”算术并获得
Foo
支持 - 能够检查结果是否为零
- 但不能让人们普遍使用枚举作为算术类型
为了好玩,我试图避免:
- 使用沼泽标准枚举
- 使用免费功能来代替
IsFlagSet
&
暂时忽略在没有事先- 或 -操作的情况下无法进行零性检查的不协调|
......
EnumToBoolProxy
我的用户仍然可以“获得” a (ie proxyThing
) ,这似乎是一种耻辱。但是,由于不可能向 中添加任何成员Foo
,并且operator bool
必须是成员,所以我似乎找不到任何其他方法来解决这个问题。
当然这不是一个真正的问题,因为他们不能对EnumToBoolProxy
. 但它仍然感觉像是一个抽象泄漏,所以我很好奇:我是否正确地说这本质上是不可能的?没有办法像这样“挑选”范围枚举的不透明度?或者是否有某种方法可以隐藏此代理类型,同时仍将其用作转换为布尔的工具来检查&
/|
操作的“结果”?你会怎么做?
解决方案
好吧,这可能不是您想要的,但是您说“隐藏此代理类型”。因此,您可以将其隐藏在下面的更多怪物中。现在生成的类型是隐藏您的代理的 lambda :)
#include <type_traits>
#include <iostream>
// Utility proxy type - convertible back to E but also permits bool conversion
// for use in conditions.
//
// e.g.
// Foo f = Foo::Bar & Foo::Baz;
// if (f & Foo::Baz) { /* ... */ }
//
auto lam = [](auto e) {
struct Key {};
//template <typename E, typename = std::enable_if_t<std::is_enum_v<E>, void>>
struct EnumToBoolProxy {
using E = decltype(e);
operator E() const {
return _val;
}
explicit operator bool() {
using UT = std::underlying_type_t<E>;
return static_cast<UT>(_val) != 0;
}
EnumToBoolProxy(const E val, Key) : _val(val) {}
private:
const E _val;
};
return EnumToBoolProxy(e, Key{});
};
enum class Foo {
Bar = 1, Baz = 2, Boi = 4
};
auto operator&(const Foo lhs, const Foo rhs) {
using UT = std::underlying_type_t<Foo>;
return lam(static_cast<Foo>(static_cast<UT>(lhs) & static_cast<UT>(rhs)));
}
template<typename T, std::enable_if_t<std::is_same_v<T, decltype(lam)>>>
auto operator&(T lhs, const Foo rhs) {
using UT = std::underlying_type_t<Foo>;
return lam(static_cast<Foo>(static_cast<UT>(lhs) & static_cast<UT>(rhs)));
}
auto operator|(const Foo lhs, const Foo rhs) {
using UT = std::underlying_type_t<Foo>;
return lam(static_cast<Foo>(static_cast<UT>(lhs) | static_cast<UT>(rhs)));
}
int main() {
lam(Foo::Bar);
// Good
if ((Foo::Bar | Foo::Baz) & Foo::Baz)
std::cout << "Yay\n";
// Fine
const bool isFlagSet((Foo::Bar | Foo::Baz) & Foo::Baz);
std::cout << isFlagSet << '\n';
// OK, still a proxy thing
auto proxyThing = (Foo::Bar | Foo::Baz) & Foo::Baz;
using Proxy = decltype(proxyThing);
//Proxy proxy2(Foo::Bar); // Does not work anymore.
}
推荐阅读
- java - 数学公式
- aws-lambda - AWS Lambda 处理程序的 Java 输出流 - 写入后不需要关闭?
- python - 使用 pip 和 anaconda 安装 python 库 cyvcf2 时遇到问题
- profiling - 在 CUDA 模式下使用 torch autograd profiler 解释 CPU 和 GPU 时间
- java - 无法使用 findViewById 访问自定义视图
- javascript - Javascript 中的过滤
- if-statement - F#If 语句期望来自浮点数的单位
- ios - 动态创建自动续订订阅
- python - 在页面上定位元素
- java - 如何解决 JSONArray 问题,无法获取数据,Android Studio