在C++中,使用有符号变量还是无符号变量作为标志的方式?

10
有没有在使用标志时使用有符号变量和无符号变量的“最佳实践”或习惯?个人会使用无符号变量,但我可以看到一些代码中甚至使用有符号变量来表示标志。我的意思是特别是在库接口中很重要。
更新:我不能接受“使用枚举”的答案,因为它是实现相关的,不能在库接口中使用。

标志(flags)对我来说应该是一个枚举(enum)。 - Nim
3
对我来说,标志(flag)不是一个枚举(enum)——因为标志可以与位运算符(如 |&~)一起使用。 - tmp
2
既然您不打算将变量用作十进制,因此使用有符号类型没有任何好处。您还可以考虑使用 std::bitset。 - Sambuca
2
除非您使用了类型化枚举。 - eerorika
1
如果您正在使用C++接口来访问您的库(而不是extern "C"),那么您将无法使用由与您的库用户使用不同编译器编译的库。 C++ ABI从编译器到编译器甚至从版本到版本都会发生变化... - MofX
显示剩余11条评论
3个回答

3
我认为无符号类型是标志集的更好表示形式。因为您需要一定数量的等效位来表示您的标志。在有符号类型中,第一个位有点特殊。
可能std::bitset也可以满足您的要求。并且将为您提供基本的逻辑运算符和转换方法。
#include <iostream>
#include <bitset>
using namespace std;

int main() {
    std::bitset<4> flags;
    flags.set(1);
    flags.set(3);
    std::cout << flags.to_string() << std::endl;
    std::cout << flags.to_ulong() << std::endl;
    return 0;
}

DEMO


2
对于那些懒得点击链接的人来说:'&'、'|'、'^'甚至'[]'都有重载。 - TobiMcNamobi

0

众所周知,标志(flags)始终是非负整数,但为什么要使用 int / unsigned int / short / unsigned short 来表示标志呢?应该使用 #define,或者更好的方法是使用 enum 类型。

enum flags
{
    FLAG0 = 0,
    FLAG1 = 1,
    FLAG2 = 2,
    ...
    FLAGN = n
};

5
不要使用 #define - eerorika
3
请再次阅读用户2079303的评论,实际上需要阅读多次以保持记忆。并广泛应用这个建议。 - PW.
1
众所周知,“标志”始终是正整数,但这并不正确。枚举的基础类型应该是一个足够大以容纳枚举所有值的整数类型;通常为“int”。此外,每个枚举类型都应与“char”或“signed”/“unsigned”整数类型兼容。 - PaperBirdMaster
1
@PaperBirdMaster,好的,也许我用“总是”这个词有点过了。但你见过负值标志吗?你听说过有人这样做吗?我认为可以肯定地说,“因为在C ++中有超过2×10 ^ 9个正整数标志需要枚举”,所以几乎总是使用正整数标志。我认为这也是一个好的实践。 - Filip Kowalski
1
@qub1n 我的观点是,除非您完全控制源代码(例如:如果您不是开发供其他团队使用的库),否则不能对标志的类型进行假设,但如果您做出选择,必须坚持一致性。 使用整数进行标记(有符号或无符号)容易出错,因为存在意外/不受控制的类型转换,并且整数没有标志语义; 如果由我选择,我会避免在此任务中使用整数(有符号或无符号),而是使用“枚举”、“位集”或“vector<bool>”。 - PaperBirdMaster
显示剩余6条评论

0
如果你决定使用枚举来表示标志,这里有一个有用的宏,可以为你的枚举类型创建位运算符的代码。
#define GENERATE_ENUM_FLAG_OPERATORS(enumType) \
    inline enumType operator| (enumType lhs, enumType rhs) \
    { \
        return static_cast<enumType>(static_cast<int>(lhs) | static_cast<int>(rhs)); \
    } \
    inline enumType& operator|= (enumType& lhs, const enumType& rhs) \
    { \
        lhs = static_cast<enumType>(static_cast<int>(lhs) | static_cast<int>(rhs)); \
        return lhs; \
    } \
    inline enumType operator& (enumType lhs, enumType rhs) \
    { \
        return static_cast<enumType>(static_cast<int>(lhs) & static_cast<int>(rhs)); \
    } \
    inline enumType& operator&= (enumType& lhs, const enumType& rhs) \
    { \
        lhs = static_cast<enumType>(static_cast<int>(lhs) & static_cast<int>(rhs)); \
        return lhs; \
    } \
    inline enumType operator~ (const enumType& rhs) \
    { \
        return static_cast<enumType>(~static_cast<int>(rhs)); \
    }

使用方法:

enum Test
{
    TEST_1 = 0x1,
    TEST_2 = 0x2,
    TEST_3 = 0x4,
};
GENERATE_ENUM_FLAG_OPERATORS(Test);

Test one = TEST_1;
Test two = TEST_2;

Test three = one | two;

我宁愿使用 std::underlying_type<enumType>::type 而不是 int。 - lisyarus
嗯,auto three = Test(TEST_1 | TEST_2) 与默认运算符也非常配合。 - Nim
我使用C++03编译器,因此我的代码不包含C++11语法 :) - rozina
1
@Nim 当然可以,但你需要每次进行类型转换。对我来说,这是 function(TEST_1 | TEST_2)function(Test(TEST_1 | TEST_2)) 的区别。而且所有这些运算符都是内联的,在运行时不会产生任何开销。 - rozina

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接