or'ing名称空间中的枚举

时间:2012-06-05 06:04:59

标签: c++ enums

我已经了解到,当两个枚举共享一个等同命名的项目时,将枚举放入命名空间可以避免编译错误。

namespace Feeling
{
    enum e
    {
        Happy = 1,
        Sad = 2,
        Blue = 4,
        Angry = 8,
        Mad = 16
    };
}

因此,您可以将其传递给声明为

的函数
void HowDoYouFeel(Feeling::e feeling);

但是当尝试OR时,就像这样:

HowDoYouFeel(Feeling::Happy | Feeling::Blue);

我收到错误。处理这个问题的最佳方法是什么?

5 个答案:

答案 0 :(得分:5)

详细说明问题时,您应指定:

  • 显示问题的最小代码示例;
  • 预期的行为;和
  • 实际行为。

在这种情况下,您错过了错误。例如,以下代码:

namespace Feeling {
    enum e { Happy = 1, Sad = 2, Blue = 4, Angry = 8, Mad = 16 };
}

int main() {
    Feeling::e ff1;
    ff1 = Feeling::Happy | Feeling::Sad;
    // ff1 = (Feeling::e)(Feeling::Happy | Feeling::Sad);
    return 0;
}

给出错误:

error: invalid conversion from ‘int’ to ‘Feeling::e’

因为|运算符的结果是int

但是,注释掉第一个作业并使用第二个作业(使用其显式演员表)编译好了。

答案 1 :(得分:1)

这个问题要求“最好的方法”,这有点主观。我为这些案例写了e operator|(e left, e right) { return e(int(left)|int(right)); },以明确Feeling::e有正交编码。

答案 2 :(得分:0)

枚举只不过是一个整数。您可以修改函数以使用整数(或无符号整数)作为输入,而不是枚举。

void HowDoYouFeel (int feelingMask);

然后像下面这样的调用就不会出错: -

HowDoYouFeel(Feeling::e::Happy | Feeling::e::Blue)

答案 3 :(得分:0)

正如其他地方所指出的那样,问题与命名空间无关,而是使用|创建一个int,并尝试将该值传递给期望enum的函数。即使你“抛弃”了这个问题,你也会在声称属于Feeling::e类型的变量中得到一个不协调的值。

enum成员获得自然的有序顺序值通常会更有帮助。这允许成员容易地用作索引值,并且编译器通常更好地优化将它们用于计算的gotos的switch语句。但是,通常情况下,enum成员的集合需要像集合一样传递。

这是我用来将enum转换为标志值的帮助器的简化版本。它对已经按顺序定义enum的代码特别有用,但现在希望将它们的集合表示为一个集合。首先,举例说明如何使用它(并注意如何将您的位操作结果转换为enum类型的解决方案必须做一些奇怪的事情才能获得类似的结果。)

namespace Feeling {
    enum e { Happy, Sad, Blue, Angry, Mad, MAX_e };
    std::string estr[] = { "Happy", "Sad", "Blue", "Angry", "Mad" };
}

void HowDoYouFeel (const Flags<Feeling::e> &feelings)
{
    for (int i = 0; i < Feeling::Max_e; ++i) {
        if (feelings.has(i)) std::cout << Feeling::estr[i] << std::endl;
    }
    if (feelings.has(Feeling::Angry)) {
        std::cout << "The Hulk is in the house." << std::endl;
    }
}

HowDoYouFeel(Feeling::Happy | Feeling::Blue | Feeling::Mad);

这是通过运算符重载,模板和帮助程序模板完成的。启用优化的实际运行时开销很低,因为代码是内联的,因此enum常量的移位操作是在编译时计算的。

template <typename E>
class Flags
{
    unsigned long long m_opts;
public:
    Flags () : m_opts(0) {}
    Flags (E e) : m_opts(1ULL << e) {}
    Flags (const FlagsTmp<E> &ot) : m_opts(ot.m_opts) {}

    bool has (unsigned i) const { return m_opts & (1ULL << i); }
    bool has (E e) const { return m_opts & (1ULL << e); }
};

Flags模板将enum类型作为模板参数,并提供了一些简单的初始化方法。 has方法用于查看enum是否是设置标志之一。

template <typename E>
class FlagsTmp
{
    friend class Flags<E>;
    mutable unsigned long long m_opts;
public:
    FlagsTmp (E e) : m_opts(1ULL << e) {}
    const FlagsTmp & operator | (E e) const {
        m_opts |= (1ULL << e);
        return *this;
    }
};

FlagsTmp用作收集所有标志的中介。它允许将标志与|个操作连接在一起,形成一个FlagsTmp实例。

template <typename E>
FlagsTmp<E> operator | (E e, E f) { return FlagsTmp<E>(e) | f; }

此运算符会重载|运算符,将两个enum标记或-d一起变为FlagsTmp

有几种方法可以扩展它以更好地满足您的需求,例如调整解决方案以使用bitset,或添加其他测试方法和运算符。

答案 4 :(得分:0)

您问题的简单答案:

  1. 与命名空间无关。
  2. 您的通话中有隐含的转化: 感觉::快乐|感觉::蓝色将导致从枚举转换为积分。 HowDoYouFeel(Feeling :: Happy | Feeling :: Blue)将导致编译器不允许从积分转换为枚举,因此错误。