用户定义的C++ 11枚举类默认构造函数

Fiz*_*erd 33 c++ default-constructor c++11 enum-class

有没有办法指定一个默认的构造函数enum class

我使用an enum class来指定一组允许库中特定数据类型的值:在这种情况下,它是Raspberry Pi的GPIO引脚ID号.它看起来像这样:

enum class PinID : int {N4 = 4, N17 = 17, /* ...etc... */ }

我这样做而不仅仅是使用一个int是确保代码是安全的:我可以static_assert(或者编译时确保 - 实际使用的方法对我来说并不重要)像有人没有这样的东西发出拼写错误(传递5而不是4等),我得到类型不匹配的自动错误消息等.

那么问题就是enum class有一个默认的构造函数 - 为了兼容性而enum我假设(因为它们具有相同的行为) - 初始化为enum class等效的0.在这种情况下,没有0价值.这意味着用户做出如下声明/定义:

PinID pid = PinID();

正在获取一个未明确定义的枚举器(当查看代码时甚至看起来"不存在"),并且可能导致运行时错误.这也意味着switch如果没有错误/默认情况,我就不可能使用明确定义的枚举器的值这样的技术- 这是我想要避免的,因为它会强迫我执行throw或执行类似返回a的操作boost::optional,这些操作不太适合静态分析.

我试图定义默认构造函数无济于事.我(拼命)试图定义一个共享名称的函数enum class,但这(不出所料)导致奇怪的编译器错误.我想保留铸的能力enum classint,与所有N#统计员映射到它们各自的#,所以仅仅是"定义",比方说,N4 = 0是不可接受的; 这是为了简单和理智.

我想我的问题是双重的:有没有办法获得我使用后的静态安全性enum class?如果没有,还有什么其他可能性?我想要的是:

  1. 是默认构造的
  2. 可以使默认构造为任意有效值
  3. 提供由enum classes 提供的"有限的指定"值
  4. 至少是类型安全的 enum class
  5. (最好)不涉及运行时多态性

我想要默认可构造性的原因是因为我打算用来boost::lexical_cast减少enum class值之间转换所涉及的语法开销,以及string我输出到操作系统的实际关联s(本例中为sysfs); boost::lexical_cast需要默认的可构造性.

我的推理中的错误是受欢迎的 - enum class在这种情况下,我开始怀疑es是错误工作的正确对象; 如有要求,将提供澄清.感谢您的时间.

Pet*_*rSW 19

使用enum classenum struct不是类定义但是作用域枚举的类型,并且不能定义默认构造函数.C++ 11标准定义了您的PinID pid = PinID();语句将进行零初始化.哪里PinID被定义为enum class.它还允许枚举类型通常包含除枚举器常量之外的值.

要理解PinID()给出零初始化需要一起阅读标准部分3.9.9,8.5.5,8.5.78.5.10:

8.5.10 -An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized

8.5.7 - To value-initialize an object of type T means:......otherwise, the object is zero-initialized.

8.5.5 -To zero-initialize an object or reference of type T means: — if T is a scalar type (3.9), the object is set to the value 0 (zero), taken as an integral constant expression, converted to T;

3.9.9 - 枚举类型是被称为标量类型的类型集的一部分.

可能的解决方案:

为了满足你的1到5分,你可以写一个类:

class PinID
{
private:
    PinID(int val)
    : m_value(val)
    {}

    int m_value;

public:
    static const PinID N4;
    static const PinID N17;
    /* ...etc... */ 

    PinID() 
    : m_value(N4.getValue())
    {}

    PinID(const PinID &id)
    : m_value(id.getValue())
    {}

    PinID &operator = (const PinID &rhs)
    {
        m_value = rhs.getValue();
        return *this;
    }

    int getValue() const
    {
        return m_value;
    }

    // Attempts to create from int and throw on failure.
    static PinID createFromInt(int i);

    friend std::istream& operator>>(std::istream &is, PinID &v)
    {
        int candidateVal(0);
        is >> candidateVal;
        v = PinID::createFromInt(candidateVal);
        return is;
    }
};

const PinID PinID::N4 = PinID(4);
/* ...etc... */
Run Code Online (Sandbox Code Playgroud)

这可以为您提供一些特定的努力来获取无效值.默认构造函数和流运算符应该允许它与lexical_cast一起使用.

似乎它取决于PinID在创建后的操作对于编写类或者只是在使用值时处理无效值的重要性.

  • 只是看着它,它看起来基本上很好,但它会起作用吗?你有一个类定义,包括一个自己作为成员的实例......哦,但它们是静态的.编译器不会生气.聪明的熊. (3认同)

chr*_*ock 5

Anenum class只是一个强类型的enum; 它不是class. C++11 只是重用了现有的class关键字,以避免引入会破坏与遗留 C++ 代码兼容性的新关键字。

至于您的问题,无法在编译时确保演员阵容涉及合适的候选人。考虑:

int x;
std::cin >> x;
auto p = static_cast<PinID>(x);
Run Code Online (Sandbox Code Playgroud)

这是完全合法的,并且无法静态地确保控制台用户做了正确的事情。

相反,您需要在运行时检查该值是否有效。为了以自动化的方式解决这个问题,我的一位同事创建了一个enum生成器,该生成器构建这些检查以及其他有用的例程,给定一个带有枚举值的文件。您需要找到适合您的解决方案。

  • @FizzixNerd“有一个默认构造函数”——从技术上讲,没有。它具有用于值初始化的定义行为。类类型对象的值初始化调用默认构造函数;数值或枚举类型对象的值初始化将其设置为零;指针类型对象的值初始化将其设置为空指针值。 (5认同)