首页 > 解决方案 > 是否有推荐的“安全”方法来指定由常量标志值组成的位和弦?

问题描述

情况:偶尔我写一个可以接受许多布尔参数的函数,而不是写这样的东西:

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

我的问题是,是否有推荐的“更安全”的方法来做到这一点?即我仍然可以轻松地将多个定义的布尔值作为一个位和弦传递给一个参数,并且可以迭代定义的位值,但是像上面显示的那些“愚蠢的错误”将被编译器而不是在运行时导致意外行为?

标签: c++booleanflags

解决方案


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

推荐阅读