c++ - 是否有推荐的“安全”方法来指定由常量标志值组成的位和弦?
问题描述
情况:偶尔我写一个可以接受许多布尔参数的函数,而不是写这样的东西:
void MyFunc(bool useFoo, bool useBar, bool useBaz, bool useBlah);
[...]
// hard to tell what this means (requires looking at the .h file)
// not obvious if/when I got the argument-ordering wrong!
MyFunc(true, true, false, true);
我希望能够使用定义的位索引的位和弦来指定它们,如下所示:
enum {
MYARG_USE_FOO = 0,
MYARG_USE_BAR,
MYARG_USE_BAZ,
MYARG_USE_BLAH,
NUM_MYARGS
};
void MyFunc(unsigned int myArgsBitChord);
[...]
// easy to see what this means
// "argument" ordering doesn't matter
MyFunc((1<<MYARG_USE_FOO)|(1<<MYARG_USE_BAR)|(1<<MYARG_USE_BLAH));
这很好用,因为它允许我轻松传递许多布尔参数(作为单个unsigned long
,而不是一长串单独的布尔值),并且我可以很容易地看到我的调用MyFunc()
指定了什么(无需回溯到一个单独的头文件)。
如果我愿意,它还允许我迭代定义的位,这有时很有用:
unsigned int allDefinedBits = 0;
for (int i=0; i<NUM_MYARGS; i++) allDefinedBits |= (1<<i);
主要缺点是它可能有点容易出错。特别是,很容易搞砸并错误地执行此操作:
// This will compile but do the wrong thing at run-time!
void MyFunc(MYARG_USE_FOO | MYARG_USE_BAR | MYARG_USE_BLAH);
......甚至犯下这个经典的额头拍打错误:
// This will compile but do the wrong thing at run-time!
void MyFunc((1<<MYARG_USE_FOO) | (1<<MYARG_USE_BAR) || (1<<MYARG_USE_BLAH));
我的问题是,是否有推荐的“更安全”的方法来做到这一点?即我仍然可以轻松地将多个定义的布尔值作为一个位和弦传递给一个参数,并且可以迭代定义的位值,但是像上面显示的那些“愚蠢的错误”将被编译器而不是在运行时导致意外行为?
解决方案
#include <iostream>
#include <type_traits>
#include <cstdint>
enum class my_options_t : std::uint32_t {
foo,
bar,
baz,
end
};
using my_options_value_t = std::underlying_type<my_options_t>::type;
inline constexpr auto operator|(my_options_t const & lhs, my_options_t const & rhs)
{
return (1 << static_cast<my_options_value_t>(lhs)) | (1 << static_cast<my_options_value_t>(rhs));
}
inline constexpr auto operator|(my_options_value_t const & lhs, my_options_t const & rhs)
{
return lhs | (1 << static_cast<my_options_value_t>(rhs));
}
inline constexpr auto operator&(my_options_value_t const & lhs, my_options_t const & rhs)
{
return lhs & (1 << static_cast<my_options_value_t>(rhs));
}
void MyFunc(my_options_value_t options)
{
if (options & my_options_t::bar)
std::cout << "yay!\n\n";
}
int main()
{
MyFunc(my_options_t::foo | my_options_t::bar | my_options_t::baz);
}
推荐阅读
- javascript - 更新嵌套数组 react.js 中的状态
- c - 从内核空间访问进程堆栈指针
- java - Java SE Embedded 在 Java 10 中是否已停用?
- wordpress - Wordpress 删除 /category/ 标签
- ssh - SSHRC 脚本不工作 > 意外的操作员
- selenium - 如何使用 HTML 代码识别 Selenium 的 Web 元素
- django - Django 复杂模型和嵌套查询
- javascript - 车把多选助手
- python - 将 MongoAlchemy 模型转换为 PyMongo
- spring-boot - H2 不使用 Spring Boot 将我的 jsp Web 表单更新到我的简单数据库