对决:enum VS const

本文通过实例比较了C++中使用枚举(enum)与常量(const)的不同效果,指出使用枚举可以在编译阶段捕获错误,提高了代码的健壮性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文标题:enum vs const

原文作者:Rob Manderson

原文出处:http://www.codeproject.com/cpp/enumvsconst.asp

译文作者:xlander

 

最近我负责对别人写的一些C++代码进行复查,代码检查能够引起错误的条件,如果一旦发生错误的话,就进行一些合情合理的善后措施。写的很好,不过,就又那么一个小地方让我郁闷的不得了。

由于某些原因,我这里不方便把真实的类展示出来,为类方便问题的说明,我们来人为地杜撰一个。

 

我所复查的类大概如下:

 

class CAClass

{

public:

    const UINT aConstantOfInterestToThisClass1 = 0;

    const UINT aConstantOfInterestToThisClass2 = 1;

 

    CAClass(UINT constValue, LPCTSTR someOtherParameter);

 

    void DoSomething();

 

private:

    UINT    m_const;

    CString m_parameter;

};

 

CAClass的构造函数传入一个它感兴趣的常量参数和一个字符串指针。DoSomething()根据构造函数传入的参数做一些处理。该类的实现代码如下:

 

CAClass::CAClass(UINT constValue, LPCTSTR someOtherParameter)

{

    m_const = constValue;

    m_parameter = someOtherParameter;

}

   

void CAClass::DoSomething()

{

    switch (m_const)

    {

    case aConstantOfInterestToThisClass1:

        //  Do something defined by this constant.

        break;

 

    case aConstantOfInterestToThisClass2:

        //  Do something defined by this constant.

        break;

 

    default:

        //  It's not a valid value, raise an exception

        RaiseException(ERROR);

        break;

    }

}

 

这段代码能够正常工作,那么问题出在哪儿呢?

如果是我的话,我会怎么做?

 

class CBClass

{

public:

    enum constsOfInterestToThisClass

    {

        bConstantOfInterestToThisClass1 = 0,

        bConstantOfInterestToThisClass2 = 1,

    };

 

    CBClass(constsOfInterestToThisClass constValue,

            LPCTSTR someOtherParameter);

 

    void DoSomething();

 

private:

    constsOfInterestToThisClass m_const;

    CString m_parameter;

};

 

实现代码:

CBClass::CBClass(constsOfInterestToThisClass constValue,

                 LPCTSTR someOtherParameter)

{

    m_const = constValue;

    m_parameter = someOtherParameter;

}

   

void CBClass::DoSomething()

{

    switch (m_const)

    {

    case bConstantOfInterestToThisClass1:

        //  Do something defined by this constant.

        break;

 

    case bConstantOfInterestToThisClass2:

        //  Do something defined by this constant.

        break;

 

    default:

        //  It's not a valid value, raise an exception

        RaiseException(ERROR);

        break;

    }

}

 

这几乎没有任何差别,任何C++编译器都能够为这两个类产生相同的代码。

那么差别在哪儿呢?

 

第一个类,CAClass,为自己定义了一组常量。第二个类也定义了同样含义的一组常量,但它的形式是enumEnum可以用来定义一个包含所有合法取值的有限集合,也可被用作伪数据类型(pseudo datatype),看一眼两个构造函数的区别:

 

CAClass(UINT constValue, LPCTSTR someOtherParameter);

CBClass(constsOfInterestToThisClass constValue, LPCTSTR someOtherParameter);

 

CAClass能够接受任何合法的UINT值,在将近40亿个可能取值中,只有两个是该类所感兴趣的。任何除了01之外的UINT取值都会使DoSomething产生异常。相反,CBClass只能接受两个枚举值中的一个。传递任何非法的常量都会使编译器报出错误或警告。

 

对比一下:

CAClass obj(1000, _T("This is a string"));

obj.DoSomething();

 

CBClass obj(1000, _T("This is a string));

obj.DoSomething();

 

在第一个例子中,CAClass obj(1000,_T(“This is a string”));将正常编译,并在运行时抛出异常。而第二个例子中,CBClass obj(1000, _T("This is a string));会毫不客气的在你的编译器里产生错误信息,这样就不会产生可执行文件,直到你纠正错误并提供合法值。

CBClass的构造函数要求第一个参数的类型是constsOfInterestToThisClass,可以是bConstantOfInterestToThisClass1,也可以是bConstantOfInterestToThisClass2,还可以是constsOfInterestToThisClass类型的变量。编译器允许你定义constsOfInterestToThisClass型的变量,不过你给这个变量赋值的时候,只能使用你先前定义的枚举值。

 

constsOfInterestToThisClass var;

var = bConstantOfInterestToThisClass1;  // OK

var = 47;  // Error

 

结论:

编译器能够在编译的时候替你进行众多的错误检查,使用enum,而不是const,能够使编译器找到使用了不合适的假定条件的代码。

Q_DECLARE_OPERATORS_FOR_FLAGS(ResizeDirections) 扩展到: [[maybe_unused]] constexpr inline QFlags<ResizeDirections::enum_type> operator|(ResizeDirections::enum_type f1, ResizeDirections::enum_type f2) noexcept { return QFlags<ResizeDirections::enum_type>(f1) | f2; } [[maybe_unused]] constexpr inline QFlags<ResizeDirections::enum_type> operator|(ResizeDirections::enum_type f1, QFlags<ResizeDirections::enum_type> f2) noexcept { return f2 | f1; } [[maybe_unused]] constexpr inline QFlags<ResizeDirections::enum_type> operator&(ResizeDirections::enum_type f1, ResizeDirections::enum_type f2) noexcept { return QFlags<ResizeDirections::enum_type>(f1) & f2; } [[maybe_unused]] constexpr inline QFlags<ResizeDirections::enum_type> operator&(ResizeDirections::enum_type f1, QFlags<ResizeDirections::enum_type> f2) noexcept { return f2 & f1; } [[maybe_unused]] constexpr inline QFlags<ResizeDirections::enum_type> operator^(ResizeDirections::enum_type f1, ResizeDirections::enum_type f2) noexcept { return QFlags<ResizeDirections::enum_type>(f1) ^ f2; } [[maybe_unused]] constexpr inline QFlags<ResizeDirections::enum_type> operator^(ResizeDirections::enum_type f1, QFlags<ResizeDirections::enum_type> f2) noexcept { return f2 ^ f1; } constexpr inline void operator+(ResizeDirections::enum_type f1, ResizeDirections::enum_type f2) noexcept = delete; constexpr inline void operator+(ResizeDirections::enum_type f1, QFlags<ResizeDirections::enum_type> f2) noexcept = delete; constexpr inline void operator+(int f1, QFlags<ResizeDirections::enum_type> f2) noexcept = delete; constexpr inline void operator-(ResizeDirections::enum_type f1, ResizeDirections::enum_type f2) noexcept = delete; constexpr inline void operator-(ResizeDirections::enum_type f1, QFlags<ResizeDirections::enum_type> f2) noexcept = delete; constexpr inline void operator-(int f1, QFlags<ResizeDirections::enum_type> f2) noexcept = delete; constexpr inline void operator+(int f1, ResizeDirections::enum_type f2) noexcept = delete; constexpr inline void operator+(ResizeDirections::enum_type f1, int f2) noexcept = delete; constexpr inline void operator-(int f1, ResizeDirections::enum_type f2) noexcept = delete; constexpr inline void operator-(ResizeDirections::enum_type f1, int f2) noexcept = delete; [[maybe_unused]] constexpr inline QIncompatibleFlag operator|(ResizeDirections::enum_type f1, int f2) noexcept { return QIncompatibleFlag(int(f1) | f2); } 报错参数太多
最新发布
03-19
<think>好的,我现在需要解决用户的问题,关于Q_DECLARE_OPERATORS_FOR_FLAGS(ResizeDirections)宏展开后报错参数太多的问题。首先,我得先理解这个宏的作用以及展开后的代码结构。 用户提供的代码展开后,生成了多个运算符重载函数,但编译器报错参数太多。可能的原因是宏展开时参数数量不符合函数定义的预期。比如,某些运算符重载可能需要不同数量的参数,或者参数类型不正确。 首先,Q_DECLARE_OPERATORS_FOR_FLAGS是Qt中的一个宏,用于为枚举类型生成位运算操作符。通常,这个宏应该生成正确的运算符,比如operator|、&、^等,允许将枚举值组合成QFlags对象。但用户手动展开的代码可能存在问题。 观察用户提供的展开代码,发现每个运算符函数都使用了ResizeDirections::enum_type作为参数类型。而QFlags通常需要模板参数是枚举类型。可能的问题是ResizeDirections本身是否正确定义了enum_type?或者是否应该直接使用ResizeDirections而不是其内部的enum_type? 另一个可能的错误来源是运算符重载的参数数量。例如,二元运算符应该接受两个参数,但可能在展开后的代码中有错误,比如参数数量不对或者类型不匹配。例如,用户提供的代码中,有些运算符可能被错误地声明为接受更多参数,或者参数类型不正确,导致编译器无法识别。 另外,注意展开后的代码中有多个运算符被声明为= delete,比如operator+和operator-,这可能与用户代码中的某些冲突有关。如果用户尝试使用这些被删除的运算符,就会导致编译错误。但用户的问题是关于参数太多,所以更可能是在函数声明时参数数量不符。 例如,检查生成的运算符函数,比如operator|,是否正确定义了两个参数?例如,用户提供的代码中,第一个operator|的定义是接受两个enum_type参数,然后返回QFlags。这部分看起来正确,因为参数数量是两个,类型正确。 但可能的错误点在于QFlags的模板参数是否正确。例如,ResizeDirections::enum_type是否正确定义?如果ResizeDirections本身是一个枚举类,那么可能需要使用ResizeDirections而不是其内部的enum_type。或者,可能应该直接使用ResizeDirections作为QFlags的模板参数,而不是enum_type。 此外,用户提供的展开代码中,有些运算符的重载可能存在重复或者冲突。例如,operator|的两个重载,一个接受两个enum_type,另一个接受enum_type和QFlags。这可能在某些情况下导致歧义,但通常编译器会正确处理重载解析,除非存在类型转换问题。 另一个需要注意的地方是[[maybe_unused]]属性的使用,这可能在某些编译器版本中导致问题,但通常不会影响参数数量。 可能的解决方法是检查ResizeDirections的定义是否正确,确保它使用了Q_DECLARE_FLAGS或类似的宏来定义QFlags的类型,以及是否正确定义了enum_type。或者,用户可能在展开宏时错误地替换了某些部分,导致参数类型错误。 例如,正确的Q_DECLARE_OPERATORS_FOR_FLAGS应该生成以枚举类型为参数的运算符,而不是enum_type。如果ResizeDirections是一个Q_ENUM或者Q_FLAG声明的枚举,那么可能需要直接使用ResizeDirections而不是内部的enum_type。 例如,正确的运算符重载应该是: operator|(ResizeDirections f1, ResizeDirections f2) 而不是使用ResizeDirections::enum_type。如果enum_type是枚举的底层类型(比如int),那么将运算符参数声明为enum_type可能会导致与整型运算符的冲突,从而需要更多的强制类型转换,或者参数类型不匹配。 总结可能的问题点: 1. ResizeDirections的enum_type是否正确定义,或者是否应该直接使用ResizeDirections作为枚举类型。 2. 展开后的运算符参数类型是否正确,是否符合QFlags的模板参数类型。 3. 是否存在重复或冲突的运算符重载,特别是与内置整数类型的运算符冲突。 解决方案可能是检查ResizeDirections的定义,确保Q_DECLARE_OPERATORS_FOR_FLAGS的参数是正确的枚举类型,并且对应的QFlags已经正确定义。或者,用户可能在手动展开宏时错误地使用了enum_type,而应该直接使用枚举类型本身。</think>这个问题的根本原因是`Q_DECLARE_OPERATORS_FOR_FLAGS`宏展开时生成的运算符重载参数列表与函数定义不匹配。以下是逐步分析: **1. 宏的预期用法** ```cpp Q_DECLARE_OPERATORS_FOR_FLAGS(ResizeDirections) ``` 要求`ResizeDirections`必须满足: - 通过`Q_DECLARE_FLAGS`定义了对应的`QFlags`类型 - `ResizeDirections`本身是一个枚举类型 **2. 参数类型错误** 展开代码中使用的`ResizeDirections::enum_type`是枚举的底层类型(通常为`int`),但运算符重载应该直接操作枚举类型本身。正确参数应为: ```cpp operator|(ResizeDirections f1, ResizeDirections f2) // 而不是enum_type ``` **3. 修正后的运算符实现** ```cpp [[maybe_unused]] constexpr inline QFlags<ResizeDirections> operator|(ResizeDirections f1, ResizeDirections f2) noexcept { return QFlags<ResizeDirections>(f1) | f2; } ``` **4. 删除冗余运算符** 展开代码中`operator+`/`operator-`的`= delete`声明是Qt防止误用的设计,但`operator|(enum_type, int)`的实现可能引发歧义,建议改为: ```cpp [[maybe_unused]] constexpr inline QIncompatibleFlag operator|(ResizeDirections f1, int f2) noexcept { return QIncompatibleFlag(int(f1) | f2); } ``` **5. 完整解决方案** ```cpp // 前置声明 Q_DECLARE_FLAGS(ResizeDirectionsFlags, ResizeDirections); Q_DECLARE_OPERATORS_FOR_FLAGS(ResizeDirectionsFlags) ``` **结论**:必须确保`ResizeDirections`正确定义为枚举,并通过`Q_DECLARE_FLAGS`生成对应的Flags类型,运算符重载应直接使用枚举类型而非其底层`enum_type`。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值