0

I've learned that putting enums in namespaces avoids compiling errors when two enums share one equaly named item.

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

So you can pass it to functions that are declared like

void HowDoYouFeel(Feeling::e feeling);

But when trying to OR it like this:

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

I get an error. What is the best way to handle this?

4

5 回答 5

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.

但是,注释掉第一个赋值并使用第二个(带有显式转换)编译没问题。

于 2012-06-05T06:12:00.540 回答
1

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

于 2012-06-05T07:30:31.807 回答
0

正如在别处所指出的那样,问题与命名空间无关,而是与使用|创建一个 int 并试图将该值传递给期望一个enum. 即使你“抛弃”了这个问题,你最终会在一个声称是 type 的变量中得到一个不协调的值Feeling::e

enum成员获得其自然有序的序列值通常更有帮助。这使得成员可以很容易地用作索引值,并且编译器通常能够更好地优化将它们用于计算 goto 的 switch 语句。但是,通常enum需要像集合一样传递成员集合。

这是我用来将enum's 转换为标志值的助手的简化版本。它对于已经定义了一个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方法用于查看 anenum是否是设置标志之一。

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标志 or-d 一起转换为FlagsTmp.

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

于 2012-06-05T07:11:39.067 回答
0

对您的问题的简单回答:

  1. 与命名空间无关。
  2. 您的通话中有隐式转换:Feeling::Happy | Feeling::Blue 会导致从枚举到整数的转换。HowDoYouFeel(Feeling::Happy | Feeling::Blue) 将导致编译器不允许从整数转换为枚举,因此出现错误。
于 2012-06-05T07:46:46.503 回答
0

An enum is nothing but an integer. You could modify the function to use integer (or unsigned integer) as input, instead of the enum.

void HowDoYouFeel (int feelingMask);

Then a call like following would give no error :-

HowDoYouFeel(Feeling::e::Happy | Feeling::e::Blue)
于 2012-06-05T06:15:12.077 回答