Làm thế nào để sử dụng enums làm cờ trong C ++?


187

Coi enumnhư các cờ hoạt động độc đáo trong C # thông qua [Flags]thuộc tính, nhưng cách tốt nhất để làm điều này trong C ++ là gì?

Ví dụ: tôi muốn viết:

enum AnimalFlags
{
    HasClaws = 1,
    CanFly =2,
    EatsFish = 4,
    Endangered = 8
};

seahawk.flags = CanFly | EatsFish | Endangered;

Tuy nhiên, tôi nhận được lỗi trình biên dịch liên quan đến int/ enumchuyển đổi. Có cách nào đẹp hơn để thể hiện điều này hơn là chỉ đúc thẳng thừng? Tốt hơn là, tôi không muốn dựa vào các cấu trúc từ các thư viện bên thứ 3 như boost hoặc Qt.

EDIT: Như đã nêu trong các câu trả lời, tôi có thể tránh lỗi trình biên dịch bằng cách khai báo seahawk.flagsint. Tuy nhiên, tôi muốn có một số cơ chế để thực thi an toàn loại, vì vậy ai đó không thể viết seahawk.flags = HasMaximizeButton.


Theo như tôi biết trong Visual C ++ 2013, [Flags]thuộc tính chỉ hoạt động tốt, tức là:[Flags] enum class FlagBits{ Ready = 1, ReadMode = 2, WriteMode = 4, EOF = 8, Disabled = 16};
Rivanov 21/07/14

@rivanov, Không, nó không hoạt động với C ++ (2015 cũng vậy). Ý bạn là C #?
Ajay

5
@rivanov, Thuộc tính [Cờ] chỉ hoạt động với .Net Framework trong C ++ CLI, C ++ bản địa không hỗ trợ các thuộc tính đó.
Zoltan Tirinda

Câu trả lời:


249

Cách "chính xác" là xác định toán tử bit cho enum, như:

enum AnimalFlags
{
    HasClaws   = 1,
    CanFly     = 2,
    EatsFish   = 4,
    Endangered = 8
};

inline AnimalFlags operator|(AnimalFlags a, AnimalFlags b)
{
    return static_cast<AnimalFlags>(static_cast<int>(a) | static_cast<int>(b));
}

V.v. phần còn lại của các toán tử bit. Sửa đổi khi cần nếu phạm vi enum vượt quá phạm vi int.


42
^ này Câu hỏi duy nhất là làm thế nào để tự động hóa / tạo khuôn mẫu cho các định nghĩa toán tử để bạn không phải liên tục xác định chúng mỗi khi bạn thêm một enum mới.
eodabash

10
Ngoài ra, việc truyền từ một int tùy ý trở lại loại enum có hợp lệ không, ngay cả khi giá trị int không tương ứng với bất kỳ định danh nào của enum?
Ingo Schalk-Schupp 30/12/13

8
Điều này là hoàn toàn vô nghĩa. Thành viên nào AnimalFlagsđược đại diện bởi biểu thức HasClaws | CanFly? Đây không phải là những gì enumcho. Sử dụng số nguyên và hằng.
Các cuộc đua nhẹ nhàng trong quỹ đạo

25
@LightnessRacesinOrbit: Điều đó không chính xác. Tên miền của loại enum là tên miền của loại cơ bản - chỉ có một số tên nhất định đã được đặt tên. Và để trả lời câu hỏi của bạn: Thành viên " (HasClaws | CanFly)".
Xèo

5
@MarcusJ: giới hạn giá trị của bạn ở quyền hạn 2 cho phép bạn sử dụng enum của mình dưới dạng cờ bit. Do đó, nếu bạn nhận được 3, bạn biết cả hai HasClaws(= 1) và CanFly(= 2). Nếu thay vào đó, bạn chỉ cần gán các giá trị 1 đến 4 liên tục và bạn nhận được 3, nó có thể là một EatsFishhoặc một lần nữa là sự kết hợp của HasClawsCanFly. Nếu phép liệt kê của bạn chỉ biểu thị trạng thái độc quyền thì các giá trị liên tiếp là tốt, nhưng sự kết hợp của các cờ cần các giá trị là độc quyền bit.
Christian Severin

122

Lưu ý (cũng hơi lạc đề): Một cách khác để tạo các cờ độc đáo có thể được thực hiện bằng cách sử dụng một chút thay đổi. Tôi, bản thân tôi, thấy điều này dễ đọc hơn.

enum Flags
{
    A = 1 << 0, // binary 0001
    B = 1 << 1, // binary 0010
    C = 1 << 2, // binary 0100
    D = 1 << 3, // binary 1000
};

Nó có thể giữ các giá trị lên đến một int, do đó, hầu hết thời gian, 32 cờ được phản ánh rõ ràng trong số lượng thay đổi.


2
Bạn có thể vui lòng xóa dấu phẩy cuối cùng (3,) và thêm dấu hai chấm sau} để làm cho mã dễ sao chép và dán không? Cảm ơn
Katu

4
Không đề cập đến thập lục phân? Lộng ngôn!
Pharap

1
@Jamie, hồng y luôn bắt đầu bằng 1, chỉ có chức vụ có thể bắt đầu bằng 0 hoặc 1, tùy thuộc vào người bạn đang nói chuyện.
Michael

2
@Michael, đó là sự thật! Trong một enum, bạn thường dự trữ 0 cho BLAH_NONE. :-) Cảm ơn bạn đã sử dụng bộ nhớ đó!
Jamie

1
@Katu • dấu phẩy thừa trên bảng liệt kê cuối cùng được cho phép theo tiêu chuẩn. Tôi không thích nó, nhưng tôi đã biết Stroustrup sẽ nói gì với tôi ... "Bạn không thích nó à? Hãy thoải mái tạo ra ngôn ngữ của riêng bạn. Tôi đã làm."
Eljay

55

Đối với những người lười biếng như tôi, đây là giải pháp templated để sao chép và dán:

template<class T> inline T operator~ (T a) { return (T)~(int)a; }
template<class T> inline T operator| (T a, T b) { return (T)((int)a | (int)b); }
template<class T> inline T operator& (T a, T b) { return (T)((int)a & (int)b); }
template<class T> inline T operator^ (T a, T b) { return (T)((int)a ^ (int)b); }
template<class T> inline T& operator|= (T& a, T b) { return (T&)((int&)a |= (int)b); }
template<class T> inline T& operator&= (T& a, T b) { return (T&)((int&)a &= (int)b); }
template<class T> inline T& operator^= (T& a, T b) { return (T&)((int&)a ^= (int)b); }

23
+1 Lười biếng là một trong ba đức tính tuyệt vời của một lập trình viên: threevirtues.com
Pharap

10
Đây là một giải pháp rất hay, chỉ cần cẩn thận rằng nó sẽ cung cấp các hoạt động bitwise cho bất kỳ loại nào. Tôi đang sử dụng một cái gì đó tương tự, nhưng với việc bổ sung các đặc điểm xác định các loại mà tôi muốn nó áp dụng kết hợp với một chút phép thuật enable_if.
Rai

@Rai: bạn luôn có thể đặt nó trong một không gian tên và usingnó ở nơi thích hợp, giống như rel_ops.
Yakov Galka

1
@ybungalobill, nhưng bạn vẫn sẽ gặp vấn đề tương tự với các thao tác áp dụng cho bất kỳ loại nào trong phạm vi sử dụng, có lẽ sẽ phù hợp với enum? Tôi nghĩ rằng những đặc điểm rất có thể là cần thiết.
Rai

19
Đừng sử dụng mã này. Nó mở ra cánh cửa cho BẤT K class lớp nào được vận hành do nhầm lẫn. Ngoài ra mã đang sử dụng kiểu truyền thống cũ sẽ không chuyển qua biên dịch nghiêm ngặt của GCC shitalshah.com/p/ .
Shital Shah

44

Lưu ý nếu bạn đang làm việc trong môi trường Windows, có một DEFINE_ENUM_FLAG_OPERATORSmacro được xác định trong winnt.h thực hiện công việc cho bạn. Vì vậy, trong trường hợp này, bạn có thể làm điều này:

enum AnimalFlags
{
    HasClaws = 1,
    CanFly =2,
    EatsFish = 4,
    Endangered = 8
};
DEFINE_ENUM_FLAG_OPERATORS(AnimalFlags)

seahawk.flags = CanFly | EatsFish | Endangered;

43

Loại biến Seahawk.flags là gì?

Trong C ++ tiêu chuẩn, bảng liệt kê không phải là loại an toàn. Họ là số nguyên hiệu quả.

AnimalFlags KHÔNG nên là loại biến của bạn. Biến của bạn phải là int và lỗi sẽ biến mất.

Đặt các giá trị thập lục phân như một số người khác được đề xuất là không cần thiết. Nó không có Gì Thay đổi.

Các giá trị enum là kiểu int theo mặc định. Vì vậy, bạn chắc chắn có thể bitwise HOẶC kết hợp chúng và đặt chúng lại với nhau và lưu kết quả trong một int.

Kiểu enum là tập con bị hạn chế của int có giá trị là một trong các giá trị được liệt kê của nó. Do đó, khi bạn tạo một số giá trị mới ngoài phạm vi đó, bạn không thể gán nó mà không truyền cho một biến kiểu enum của bạn.

Bạn cũng có thể thay đổi các loại giá trị enum nếu bạn muốn, nhưng không có điểm nào cho câu hỏi này.

EDIT: Người đăng cho biết họ quan tâm đến an toàn kiểu và họ không muốn một giá trị không tồn tại bên trong kiểu int.

Nhưng sẽ không an toàn nếu đặt một giá trị bên ngoài phạm vi của AnimalFlags bên trong một biến kiểu AnimalFlags.

Có một cách an toàn để kiểm tra ngoài giá trị phạm vi mặc dù bên trong kiểu int ...

int iFlags = HasClaws | CanFly;
//InvalidAnimalFlagMaxValue-1 gives you a value of all the bits 
// smaller than itself set to 1
//This check makes sure that no other bits are set.
assert(iFlags & ~(InvalidAnimalFlagMaxValue-1) == 0);

enum AnimalFlags {
    HasClaws = 1,
    CanFly =2,
    EatsFish = 4,
    Endangered = 8,

    // put new enum values above here
    InvalidAnimalFlagMaxValue = 16
};

Ở trên không ngăn bạn đặt một cờ không hợp lệ từ một enum khác có giá trị 1,2,4 hoặc 8 mặc dù.

Nếu bạn muốn loại an toàn tuyệt đối thì bạn có thể chỉ cần tạo một std :: set và lưu trữ mỗi cờ bên trong đó. Nó không phải là không gian hiệu quả, nhưng nó là loại an toàn và cung cấp cho bạn khả năng tương tự như một bitflag int.

C ++ 0x lưu ý: enum gõ mạnh

Trong C ++ 0x cuối cùng bạn cũng có thể có các giá trị enum an toàn ....

enum class AnimalFlags {
    CanFly = 2,
    HasClaws = 4
};

if(CanFly == 2) { }//Compiling error

4
Các giá trị enum không phải là số nguyên, nhưng chúng rất dễ dàng chuyển đổi thành số nguyên. Loại của HasClaws | CanFlymột số loại số nguyên, nhưng loại HasClawsAnimalFlags, không phải là một loại số nguyên.
Karu

À, nhưng điều gì sẽ xảy ra nếu chúng ta xác định phạm vi chính xác của enum không chỉ là các giá trị cờ riêng lẻ mà còn là các kết hợp bitwise của chúng. Sau đó, câu trả lời của eidolon là chính xác và duy trì rằng chỉ có các kết hợp của enum cờ chính xác mới có thể được chuyển thành loại đó.
Scott

3
@Scott: Điều đáng chú ý là tiêu chuẩn C ++ xác định phạm vi giá trị hợp lệ của một ví dụ enum theo cách đó. "đối với phép liệt kê trong đó emin là liệt kê nhỏ nhất và emax là lớn nhất, các giá trị của phép liệt kê là các giá trị trong phạm vi bmin đến bmax, được định nghĩa như sau: Đặt K là 1 cho biểu diễn bổ sung hai và 0 cho một ' đại diện bổ sung hoặc ký hiệu cường độ. bmax là giá trị nhỏ nhất lớn hơn hoặc bằng max(|emin| − K, |emax|)và bằng (1u<<M) - 1, trong đó Mlà một số nguyên không âm. "
Ben Voigt

Đối với những người (như tôi) chỉ muốn một cái gì đó thiết thực cho phép các giá trị enum được thao tác theo bit và trông không quá xấu với các mẫu và kiểu đúc, đây là một giải pháp tốt; chỉ cần xác định các biến là loại int.
Eric Sokolowsky

Cũng lưu ý rằng trong C ++, thông thường enumkhông mặc định về mặt kỹ thuật intlà loại cơ bản của nó (trước C ++ 11 (IIRC) hoặc sau C ++ 11 khi không có loại cơ bản nào được chỉ định), mặc dù enum class vậy . Thay vào đó, kiểu cơ bản mặc định là một cái gì đó đủ lớn để đại diện cho tất cả các điều tra viên, với quy tắc cứng thực sự duy nhất là nó chỉ lớn hơn intnếu nó rõ ràng cần phải có. Về cơ bản, loại cơ bản được chỉ định là (diễn giải) "bất cứ điều gì hoạt động, nhưng có lẽ int trừ khi các điều tra viên quá lớn int".
Thời gian Justin - Phục hồi Monica

26

Tôi thấy câu trả lời hiện được chấp nhận bởi eidolon quá nguy hiểm. Trình tối ưu hóa của trình biên dịch có thể đưa ra các giả định về các giá trị có thể có trong enum và bạn có thể lấy lại rác với các giá trị không hợp lệ. Và thường thì không ai muốn định nghĩa tất cả các hoán vị có thể có trong enum cờ.

Như Brian R. Bondy tuyên bố dưới đây, nếu bạn đang sử dụng C ++ 11 (mà mọi người nên, điều đó thật tốt), giờ đây bạn có thể thực hiện việc này dễ dàng hơn với enum class:

enum class ObjectType : uint32_t
{
    ANIMAL = (1 << 0),
    VEGETABLE = (1 << 1),
    MINERAL = (1 << 2)
};


constexpr enum ObjectType operator |( const enum ObjectType selfValue, const enum ObjectType inValue )
{
    return (enum ObjectType)(uint32_t(selfValue) | uint32_t(inValue));
}

// ... add more operators here. 

Điều này đảm bảo kích thước và phạm vi giá trị ổn định bằng cách chỉ định một loại cho enum, ức chế tự động hạ cấp enum cho ints, v.v. bằng cách sử dụng enum classvà sử dụng constexprđể đảm bảo mã cho các toán tử được nội tuyến và do đó nhanh như các số thông thường.

Dành cho người mắc kẹt với phương ngữ trước 11 C ++

Nếu tôi bị mắc kẹt với trình biên dịch không hỗ trợ C ++ 11, tôi sẽ sử dụng kiểu int trong một lớp mà sau đó chỉ cho phép sử dụng các toán tử bitwise và các kiểu từ enum đó để đặt giá trị của nó:

template<class ENUM,class UNDERLYING=typename std::underlying_type<ENUM>::type>
class SafeEnum
{
public:
    SafeEnum() : mFlags(0) {}
    SafeEnum( ENUM singleFlag ) : mFlags(singleFlag) {}
    SafeEnum( const SafeEnum& original ) : mFlags(original.mFlags) {}

    SafeEnum&   operator |=( ENUM addValue )    { mFlags |= addValue; return *this; }
    SafeEnum    operator |( ENUM addValue )     { SafeEnum  result(*this); result |= addValue; return result; }
    SafeEnum&   operator &=( ENUM maskValue )   { mFlags &= maskValue; return *this; }
    SafeEnum    operator &( ENUM maskValue )    { SafeEnum  result(*this); result &= maskValue; return result; }
    SafeEnum    operator ~()    { SafeEnum  result(*this); result.mFlags = ~result.mFlags; return result; }
    explicit operator bool()                    { return mFlags != 0; }

protected:
    UNDERLYING  mFlags;
};

Bạn có thể định nghĩa điều này khá giống như một enum + typedef thông thường:

enum TFlags_
{
    EFlagsNone  = 0,
    EFlagOne    = (1 << 0),
    EFlagTwo    = (1 << 1),
    EFlagThree  = (1 << 2),
    EFlagFour   = (1 << 3)
};

typedef SafeEnum<enum TFlags_>  TFlags;

Và cách sử dụng cũng tương tự:

TFlags      myFlags;

myFlags |= EFlagTwo;
myFlags |= EFlagThree;

if( myFlags & EFlagTwo )
    std::cout << "flag 2 is set" << std::endl;
if( (myFlags & EFlagFour) == EFlagsNone )
    std::cout << "flag 4 is not set" << std::endl;

Và bạn cũng có thể ghi đè loại cơ bản cho các enum ổn định nhị phân (như C ++ 11 enum foo : type) bằng cách sử dụng tham số mẫu thứ hai, nghĩa là typedef SafeEnum<enum TFlags_,uint8_t> TFlags;.

Tôi đã đánh dấu operator boolghi đè bằng explicittừ khóa của C ++ 11 để ngăn nó dẫn đến chuyển đổi int, vì chúng có thể khiến các bộ cờ kết thúc thành 0 hoặc 1 khi viết chúng ra. Nếu bạn không thể sử dụng C ++ 11, hãy bỏ quá tải đó và viết lại điều kiện đầu tiên trong cách sử dụng ví dụ như (myFlags & EFlagTwo) == EFlagTwo.


Lưu ý, tôi khuyên bạn nên sử dụng toán tử mẫu được xác định khi bắt đầu sử dụng std::underlying_typethay vì mã hóa cứng một loại cụ thể hoặc loại cơ bản được cung cấp và sử dụng làm bí danh loại thay vì trực tiếp. Bằng cách đó, các thay đổi đối với loại cơ bản sẽ lan truyền tự động, thay vì phải được thực hiện thủ công.
Thời gian Justin - Phục hồi Monica

17

Cách dễ nhất để làm điều này như được hiển thị ở đây , sử dụng bitet lớp thư viện chuẩn .

Để mô phỏng tính năng C # theo cách an toàn kiểu, bạn phải viết một trình bao bọc mẫu xung quanh bitet, thay thế các đối số int bằng một enum được cung cấp dưới dạng tham số kiểu cho mẫu. Cái gì đó như:

    template <class T, int N>
class FlagSet
{

    bitset<N> bits;

    FlagSet(T enumVal)
    {
        bits.set(enumVal);
    }

    // etc.
};

enum MyFlags
{
    FLAG_ONE,
    FLAG_TWO
};

FlagSet<MyFlags, 2> myFlag;

4
Nhìn vào điều này để biết mã hoàn chỉnh hơn: codereview.stackexchange.com/questions/96146/iêu
Shital Shah

11

Theo tôi không có câu trả lời nào cho đến nay là lý tưởng. Để trở nên lý tưởng tôi sẽ mong đợi giải pháp:

  1. Hỗ trợ ==, !=, =, &, &=, |, |=~các nhà khai thác theo nghĩa thông thường (ví dụ a & b)
  2. Hãy loại an toàn tức là không cho phép các giá trị không được liệt kê như chữ hoặc số nguyên được gán (ngoại trừ kết hợp bitwise của các giá trị được liệt kê) hoặc cho phép biến enum được gán cho loại số nguyên
  3. Cho phép các biểu thức như if (a & b)...
  4. Không yêu cầu macro ác, thực hiện các tính năng cụ thể hoặc các bản hack khác

Do đó, hầu hết các giải pháp đều rơi vào điểm 2 hoặc 3. Theo ý kiến ​​của tôi, WebDancers đóng cửa theo ý kiến ​​của tôi nhưng thất bại ở điểm 3 và cần phải được lặp lại cho mỗi enum.

Giải pháp đề xuất của tôi là một phiên bản tổng quát của WebDancer cũng giải quyết điểm 3:

#include <cstdint>
#include <type_traits>

template<typename T = typename std::enable_if<std::is_enum<T>::value, T>::type>
class auto_bool
{
    T val_;
public:
    constexpr auto_bool(T val) : val_(val) {}
    constexpr operator T() const { return val_; }
    constexpr explicit operator bool() const
    {
        return static_cast<std::underlying_type_t<T>>(val_) != 0;
    }
};

template <typename T = typename std::enable_if<std::is_enum<T>::value, T>::type>
constexpr auto_bool<T> operator&(T lhs, T rhs)
{
    return static_cast<T>(
        static_cast<typename std::underlying_type<T>::type>(lhs) &
        static_cast<typename std::underlying_type<T>::type>(rhs));
}

template <typename T = typename std::enable_if<std::is_enum<T>::value, T>::type>
constexpr T operator|(T lhs, T rhs)
{
    return static_cast<T>(
        static_cast<typename std::underlying_type<T>::type>(lhs) |
        static_cast<typename std::underlying_type<T>::type>(rhs));
}

enum class AnimalFlags : uint8_t 
{
    HasClaws = 1,
    CanFly = 2,
    EatsFish = 4,
    Endangered = 8
};

enum class PlantFlags : uint8_t
{
    HasLeaves = 1,
    HasFlowers = 2,
    HasFruit = 4,
    HasThorns = 8
};

int main()
{
    AnimalFlags seahawk = AnimalFlags::CanFly;        // Compiles, as expected
    AnimalFlags lion = AnimalFlags::HasClaws;         // Compiles, as expected
    PlantFlags rose = PlantFlags::HasFlowers;         // Compiles, as expected
//  rose = 1;                                         // Won't compile, as expected
    if (seahawk != lion) {}                           // Compiles, as expected
//  if (seahawk == rose) {}                           // Won't compile, as expected
//  seahawk = PlantFlags::HasThorns;                  // Won't compile, as expected
    seahawk = seahawk | AnimalFlags::EatsFish;        // Compiles, as expected
    lion = AnimalFlags::HasClaws |                    // Compiles, as expected
           AnimalFlags::Endangered;
//  int eagle = AnimalFlags::CanFly |                 // Won't compile, as expected
//              AnimalFlags::HasClaws;
//  int has_claws = seahawk & AnimalFlags::CanFly;    // Won't compile, as expected
    if (seahawk & AnimalFlags::CanFly) {}             // Compiles, as expected
    seahawk = seahawk & AnimalFlags::CanFly;          // Compiles, as expected

    return 0;
}

Điều này tạo ra sự quá tải của các toán tử cần thiết nhưng sử dụng SFINAE để giới hạn chúng trong các kiểu liệt kê. Lưu ý rằng vì lợi ích của sự ngắn gọn, tôi đã không xác định tất cả các toán tử nhưng cái duy nhất khác biệt là &. Các toán tử hiện tại là toàn cầu (nghĩa là áp dụng cho tất cả các kiểu liệt kê) nhưng điều này có thể được giảm bớt bằng cách đặt quá tải trong một không gian tên (những gì tôi làm) hoặc bằng cách thêm các điều kiện SFINAE bổ sung (có thể sử dụng các loại cơ bản cụ thể hoặc bí danh được tạo đặc biệt ). Đây underlying_type_tlà một tính năng của C ++ 14 nhưng có vẻ như được hỗ trợ tốt và dễ dàng mô phỏng cho C ++ 11 một cách đơn giảntemplate<typename T> using underlying_type_t = underlying_type<T>::type;


Mặc dù giải pháp đề xuất của bạn hoạt động rất tốt, nó cũng giới thiệu mẫu này cho các enum không có nghĩa là được coi là cờ. Đó có lẽ là lý do cho việc sử dụng các macro (ác) như DEFINE_ENUM_FLAG_OPERATORS từ Microsoft.
WebDancer 15/03/18

@WebDancer, bạn tất nhiên là đúng, nhưng sau đó tôi đã nói điều đó trong câu trả lời của tôi. Tôi cũng đề xuất hai cách giải quyết vấn đề - đặt nó vào một không gian tên hoặc sử dụng điều kiện SFINAE hạn chế hơn.
Trevor

Quan điểm của tôi là trừ khi bạn tạo một không gian tên thực sự hẹp (ví dụ: không gian tên AllMyFlagEnums) hoặc có một điều kiện SFINAE mà theo một cách nào đó, chỉ chọn một vài enum chính xác mà mã bị phá vỡ trong tâm trí tôi. Thay vì mạo hiểm điều này, tôi sao chép và dán một "mẫu văn bản" nơi tôi chỉ cần thay thế tên enum và đôi khi là các macro "ác". Tôi ước có một cách tốt hơn.
WebDancer

Đầu tiên, nó sẽ chỉ gây ra sự cố nếu ở nơi khác trong mã của bạn, bạn cần thực hiện một trong những điều mà nó dự định dừng, ví dụ: gán một chữ, số nguyên hoặc một phần tử từ enum khác. Mặt khác, enum được sửa đổi hoạt động như một enum thông thường, ví dụ, các phần tử không nhất thiết phải là lũy thừa của hai và phép gán, so sánh và các phép toán bit hoạt động như bình thường. Nếu bạn thực sự phải gán chữ hoặc trộn enum, bạn vẫn có thể sử dụng một cách rõ ràng, với lợi thế được thêm vào là ý định của bạn sẽ rõ ràng hơn. Vì vậy, rất có thể sẽ không cần phải giảm phạm vi.
Trevor

Thứ hai, nếu ngay cả khi bạn cần giảm phạm vi, không gian tên có thể không cần phải thu hẹp - mặc dù điều đó sẽ phụ thuộc vào những gì bạn đang làm. Nếu bạn đang làm việc trên một thư viện thì có lẽ bạn đã có mã của mình phụ thuộc vào enum trong một không gian tên, sau đó mã enum chỉ đi trong cùng một không gian tên. Nếu bạn yêu cầu hành vi enum cho một lớp (có lẽ bạn muốn sử dụng enum làm đối số phương thức hoặc biến thành viên của lớp) thì hãy đặt mã enum trong lớp để có hiệu quả tương tự. Điểm mấu chốt là bạn không cần phải bọc một không gian tên xung quanh chỉ các enum - mặc dù bạn có thể.
Trevor

8

Tiêu chuẩn C ++ nói rõ ràng về điều này, xem phần "17.5.2.1.3 Các loại Bitmask":

http://www.open-std.org/jtc1/sc22/wg21/docs/ con / 2012 / n3485.pdf

Cho "mẫu" này bạn nhận được:

enum AnimalFlags : unsigned int
{
    HasClaws = 1,
    CanFly = 2,
    EatsFish = 4,
    Endangered = 8
};

constexpr AnimalFlags operator|(AnimalFlags X, AnimalFlags Y) {
    return static_cast<AnimalFlags>(
        static_cast<unsigned int>(X) | static_cast<unsigned int>(Y));
}

AnimalFlags& operator|=(AnimalFlags& X, AnimalFlags Y) {
    X = X | Y; return X;
}

Và tương tự cho các nhà khai thác khác. Cũng lưu ý "constexpr", cần thiết nếu bạn muốn trình biên dịch có thể thực thi các toán tử biên dịch thời gian.

Nếu bạn đang sử dụng C ++ / CLI và muốn có thể gán cho enum thành viên của các lớp ref, bạn cần sử dụng các tham chiếu theo dõi thay thế:

AnimalFlags% operator|=(AnimalFlags% X, AnimalFlags Y) {
    X = X | Y; return X;
}

LƯU Ý: Mẫu này chưa hoàn chỉnh, xem phần "17.5.2.1.3 Các loại Bitmask" để biết một bộ toán tử hoàn chỉnh.


6

Tôi thấy mình hỏi cùng một câu hỏi và đưa ra một giải pháp dựa trên C ++ 11 chung, tương tự như của soru:

template <typename TENUM>
class FlagSet {

private:
    using TUNDER = typename std::underlying_type<TENUM>::type;
    std::bitset<std::numeric_limits<TUNDER>::max()> m_flags;

public:
    FlagSet() = default;

    template <typename... ARGS>
    FlagSet(TENUM f, ARGS... args) : FlagSet(args...)
    {   
        set(f);
    }   
    FlagSet& set(TENUM f)
    {   
        m_flags.set(static_cast<TUNDER>(f));
        return *this;
    }   
    bool test(TENUM f)
    {   
        return m_flags.test(static_cast<TUNDER>(f));
    }   
    FlagSet& operator|=(TENUM f)
    {   
        return set(f);
    }   
};

Giao diện có thể được cải thiện để hương vị. Sau đó, nó có thể được sử dụng như vậy:

FlagSet<Flags> flags{Flags::FLAG_A, Flags::FLAG_C};
flags |= Flags::FLAG_D;

2
Nhìn vào điều này để biết mã tốt hơn và đầy đủ hơn: codereview.stackexchange.com/questions/96146/iêu
Shital Shah

5
Ngoại trừ việc tôi sử dụng num_limits, mã gần như giống nhau. Tôi đoán đó là một cách phổ biến để có một lớp enum an toàn loại. Tôi sẽ lập luận rằng sử dụng num_limits tốt hơn là đặt SENTINEL vào cuối mỗi enum.
Omair

1
Đó là một bitet lớn !
Các cuộc đua nhẹ nhàng trong quỹ đạo


5

Nếu trình biên dịch của bạn chưa hỗ trợ các kiểu gõ mạnh, bạn có thể xem bài viết sau từ nguồn c ++:

Từ tóm tắt:

Bài viết này trình bày một giải pháp cho vấn đề ràng buộc các hoạt động bit để
chỉ cho phép các hoạt động an toàn và hợp pháp và biến tất cả các thao tác bit không hợp lệ thành các lỗi thời gian biên dịch. Trên hết, cú pháp của các hoạt động bit vẫn không thay đổi và mã làm việc với các bit không cần phải sửa đổi, ngoại trừ có thể sửa các lỗi vẫn chưa được phát hiện.


5

Tôi sử dụng macro sau:

#define ENUM_FLAG_OPERATORS(T)                                                                                                                                            \
    inline T operator~ (T a) { return static_cast<T>( ~static_cast<std::underlying_type<T>::type>(a) ); }                                                                       \
    inline T operator| (T a, T b) { return static_cast<T>( static_cast<std::underlying_type<T>::type>(a) | static_cast<std::underlying_type<T>::type>(b) ); }                   \
    inline T operator& (T a, T b) { return static_cast<T>( static_cast<std::underlying_type<T>::type>(a) & static_cast<std::underlying_type<T>::type>(b) ); }                   \
    inline T operator^ (T a, T b) { return static_cast<T>( static_cast<std::underlying_type<T>::type>(a) ^ static_cast<std::underlying_type<T>::type>(b) ); }                   \
    inline T& operator|= (T& a, T b) { return reinterpret_cast<T&>( reinterpret_cast<std::underlying_type<T>::type&>(a) |= static_cast<std::underlying_type<T>::type>(b) ); }   \
    inline T& operator&= (T& a, T b) { return reinterpret_cast<T&>( reinterpret_cast<std::underlying_type<T>::type&>(a) &= static_cast<std::underlying_type<T>::type>(b) ); }   \
    inline T& operator^= (T& a, T b) { return reinterpret_cast<T&>( reinterpret_cast<std::underlying_type<T>::type&>(a) ^= static_cast<std::underlying_type<T>::type>(b) ); }

Nó tương tự như những người được đề cập ở trên nhưng có một số cải tiến:

  • Đó là loại an toàn (không giả sử rằng loại cơ bản là một int )
  • Nó không yêu cầu chỉ định thủ công loại cơ bản (trái ngược với câu trả lời của @LunarEclipse)

Nó không cần phải bao gồm type_traits:

#include <type_traits>

4

Tôi muốn giải thích về câu trả lời của Uliwitness , sửa mã của anh ấy cho C ++ 98 và sử dụng thành ngữ Safe Bool , vì thiếu std::underlying_type<>mẫu vàexplicit từ khóa trong các phiên bản C ++ bên dưới C ++ 11.

Tôi cũng đã sửa đổi nó để các giá trị enum có thể tuần tự mà không cần gán rõ ràng, vì vậy bạn có thể có

enum AnimalFlags_
{
    HasClaws,
    CanFly,
    EatsFish,
    Endangered
};
typedef FlagsEnum<AnimalFlags_> AnimalFlags;

seahawk.flags = AnimalFlags() | CanFly | EatsFish | Endangered;

Sau đó, bạn có thể nhận được giá trị cờ thô với

seahawk.flags.value();

Đây là mã.

template <typename EnumType, typename Underlying = int>
class FlagsEnum
{
    typedef Underlying FlagsEnum::* RestrictedBool;

public:
    FlagsEnum() : m_flags(Underlying()) {}

    FlagsEnum(EnumType singleFlag):
        m_flags(1 << singleFlag)
    {}

    FlagsEnum(const FlagsEnum& original):
        m_flags(original.m_flags)
    {}

    FlagsEnum& operator |=(const FlagsEnum& f) {
        m_flags |= f.m_flags;
        return *this;
    }

    FlagsEnum& operator &=(const FlagsEnum& f) {
        m_flags &= f.m_flags;
        return *this;
    }

    friend FlagsEnum operator |(const FlagsEnum& f1, const FlagsEnum& f2) {
        return FlagsEnum(f1) |= f2;
    }

    friend FlagsEnum operator &(const FlagsEnum& f1, const FlagsEnum& f2) {
        return FlagsEnum(f1) &= f2;
    }

    FlagsEnum operator ~() const {
        FlagsEnum result(*this);
        result.m_flags = ~result.m_flags;
        return result;
    }

    operator RestrictedBool() const {
        return m_flags ? &FlagsEnum::m_flags : 0;
    }

    Underlying value() const {
        return m_flags;
    }

protected:
    Underlying  m_flags;
};

3

Đây là một tùy chọn cho bitmasks nếu bạn không thực sự sử dụng cho các giá trị enum riêng lẻ (ví dụ: bạn không cần tắt chúng) ... và nếu bạn không lo lắng về việc duy trì khả năng tương thích nhị phân, tức là: bạn đừng quan tâm nơi bit của bạn sống ... mà bạn có thể là. Ngoài ra, bạn tốt hơn không nên quá quan tâm đến phạm vi và kiểm soát truy cập. Hmmm, enums có một số thuộc tính đẹp cho các trường bit ... tự hỏi liệu có ai đã từng thử nó chưa :)

struct AnimalProperties
{
    bool HasClaws : 1;
    bool CanFly : 1;
    bool EatsFish : 1;
    bool Endangered : 1;
};

union AnimalDescription
{
    AnimalProperties Properties;
    int Flags;
};

void TestUnionFlags()
{
    AnimalDescription propertiesA;
    propertiesA.Properties.CanFly = true;

    AnimalDescription propertiesB = propertiesA;
    propertiesB.Properties.EatsFish = true;

    if( propertiesA.Flags == propertiesB.Flags )
    {
        cout << "Life is terrible :(";
    }
    else
    {
        cout << "Life is great!";
    }

    AnimalDescription propertiesC = propertiesA;
    if( propertiesA.Flags == propertiesC.Flags )
    {
        cout << "Life is great!";
    }
    else
    {
        cout << "Life is terrible :(";
    }
}

Chúng ta có thể thấy rằng cuộc sống thật tuyệt vời, chúng ta có những giá trị riêng biệt và chúng ta có một int đẹp và & | với nội dung trái tim của chúng tôi, vẫn có bối cảnh về ý nghĩa của các bit của nó. Mọi thứ đều nhất quán và có thể dự đoán được ... đối với tôi ... miễn là tôi tiếp tục sử dụng trình biên dịch VC ++ của Microsoft w / Cập nhật 3 trên Win10 x64 và không chạm vào cờ trình biên dịch của tôi :)

Mặc dù mọi thứ đều tuyệt vời ... hiện tại chúng tôi có một số bối cảnh về ý nghĩa của các lá cờ, vì nó nằm trong một liên minh với thế giới thực khủng khiếp nơi chương trình của bạn có thể chịu trách nhiệm cho nhiều hơn một nhiệm vụ riêng lẻ mà bạn có thể vẫn vô tình (khá dễ dàng) đập hai trường cờ của các hiệp hội khác nhau (giả sử AnimalProperives và ObjectProperies, vì cả hai đều là int), trộn lẫn tất cả các bit của bạn, đó là một lỗi khủng khiếp để truy tìm ... và làm sao tôi biết nhiều người trên bài đăng này không thường xuyên làm việc với bitmasks, vì việc xây dựng chúng rất dễ dàng và duy trì chúng là khó khăn.

class AnimalDefinition {
public:
    static AnimalDefinition *GetAnimalDefinition( AnimalFlags flags );   //A little too obvious for my taste... NEXT!
    static AnimalDefinition *GetAnimalDefinition( AnimalProperties properties );   //Oh I see how to use this! BORING, NEXT!
    static AnimalDefinition *GetAnimalDefinition( int flags ); //hmm, wish I could see how to construct a valid "flags" int without CrossFingers+Ctrl+Shift+F("Animal*"). Maybe just hard-code 16 or something?

    AnimalFlags animalFlags;  //Well this is *way* too hard to break unintentionally, screw this!
    int flags; //PERFECT! Nothing will ever go wrong here... 
    //wait, what values are used for this particular flags field? Is this AnimalFlags or ObjectFlags? Or is it RuntimePlatformFlags? Does it matter? Where's the documentation? 
    //Well luckily anyone in the code base and get confused and destroy the whole program! At least I don't need to static_cast anymore, phew!

    private:
    AnimalDescription m_description; //Oh I know what this is. All of the mystery and excitement of life has been stolen away :(
}

Vì vậy, sau đó bạn đặt tuyên bố liên minh của mình ở chế độ riêng tư để ngăn truy cập trực tiếp vào "Cờ" và phải thêm getters / setters và quá tải toán tử, sau đó tạo macro cho tất cả điều đó, và về cơ bản bạn đã quay lại ngay khi bạn cố gắng làm điều này với một Enum.

Thật không may nếu bạn muốn mã của mình có thể di động, tôi không nghĩ có bất kỳ cách nào để A) đảm bảo bố cục bit hoặc B) xác định bố cục bit tại thời điểm biên dịch (để bạn có thể theo dõi mã và ít nhất là chính xác cho các thay đổi trên phiên bản / nền tảng, v.v.) Bù đắp trong một cấu trúc với các trường bit

Trong thời gian chạy, bạn có thể chơi các thủ thuật với các trường và XOR các cờ để xem bit nào đã thay đổi, nghe có vẻ khá nhảm nhí với tôi mặc dù các câu có giải pháp 100% nhất quán, độc lập với nền tảng và hoàn toàn xác định, ví dụ: ENUM.

TL; DR: Đừng nghe những người ghét. C ++ không phải là tiếng Anh. Chỉ vì định nghĩa theo nghĩa đen của từ khóa viết tắt được kế thừa từ C có thể không phù hợp với cách sử dụng của bạn không có nghĩa là bạn không nên sử dụng nó khi định nghĩa C C ++ của từ khóa hoàn toàn bao gồm trường hợp sử dụng của bạn. Bạn cũng có thể sử dụng các cấu trúc để mô hình hóa những thứ khác ngoài cấu trúc và các lớp học cho những thứ khác ngoài trường học và đẳng cấp xã hội. Bạn có thể sử dụng float cho các giá trị được nối đất. Bạn có thể sử dụng char cho các biến không bị cháy cũng như một người trong tiểu thuyết, vở kịch hoặc phim. Bất kỳ lập trình viên nào đi vào từ điển để xác định ý nghĩa của từ khóa trước thông số ngôn ngữ là ... tôi sẽ giữ lời nói của mình ở đó.

Nếu bạn thực sự muốn mã của mình được mô hình hóa theo ngôn ngữ nói thì tốt nhất bạn nên viết bằng Objective-C, điều này cũng vô tình sử dụng enums rất nhiều cho bitfield.


3

Chỉ cú pháp đường. Không có siêu dữ liệu bổ sung.

namespace UserRole // grupy
{ 
    constexpr uint8_t dea = 1;
    constexpr uint8_t red = 2;
    constexpr uint8_t stu = 4;
    constexpr uint8_t kie = 8;
    constexpr uint8_t adm = 16;
    constexpr uint8_t mas = 32;
}

Toán tử cờ trên loại tích phân chỉ hoạt động.


IMHO đây là câu trả lời tốt nhất. Sạch sẽ và đơn giản, cú pháp máy khách dễ dàng. Tôi sẽ chỉ sử dụng "const int" chứ không phải "constexpr uint8_t", nhưng khái niệm này là như nhau.
yoyo

(xin lỗi, "constexpr int")
yoyo

3

Hiện tại không có hỗ trợ ngôn ngữ cho cờ enum, các lớp Meta vốn có thể thêm tính năng này nếu nó từng là một phần của tiêu chuẩn c ++.

Giải pháp của tôi sẽ là tạo các hàm mẫu được khởi tạo chỉ enum thêm hỗ trợ cho các hoạt động bitwise an toàn kiểu cho lớp enum bằng cách sử dụng kiểu cơ bản của nó:

Tập tin: EnumClassBitwise.h

#pragma once
#ifndef _ENUM_CLASS_BITWISE_H_
#define _ENUM_CLASS_BITWISE_H_

#include <type_traits>

//unary ~operator    
template <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>
constexpr inline Enum& operator~ (Enum& val)
{
    val = static_cast<Enum>(~static_cast<std::underlying_type_t<Enum>>(val));
    return val;
}

// & operator
template <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>
constexpr inline Enum operator& (Enum lhs, Enum rhs)
{
    return static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) & static_cast<std::underlying_type_t<Enum>>(rhs));
}

// &= operator
template <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>
constexpr inline Enum operator&= (Enum& lhs, Enum rhs)
{
    lhs = static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) & static_cast<std::underlying_type_t<Enum>>(rhs));
    return lhs;
}

//| operator

template <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>
constexpr inline Enum operator| (Enum lhs, Enum rhs)
{
    return static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) | static_cast<std::underlying_type_t<Enum>>(rhs));
}
//|= operator

template <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>
constexpr inline Enum& operator|= (Enum& lhs, Enum rhs)
{
    lhs = static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) | static_cast<std::underlying_type_t<Enum>>(rhs));
    return lhs;
}

#endif // _ENUM_CLASS_BITWISE_H_

Để thuận tiện và để giảm lỗi, bạn có thể muốn bọc các hoạt động cờ bit của mình cho enums và cho số nguyên:

Tệp: BitFlags.h

#pragma once
#ifndef _BIT_FLAGS_H_
#define _BIT_FLAGS_H_

#include "EnumClassBitwise.h"

 template<typename T>
 class BitFlags
 {
 public:

     constexpr inline BitFlags() = default;
     constexpr inline BitFlags(T value) { mValue = value; }
     constexpr inline BitFlags operator| (T rhs) const { return mValue | rhs; }
     constexpr inline BitFlags operator& (T rhs) const { return mValue & rhs; }
     constexpr inline BitFlags operator~ () const { return ~mValue; }
     constexpr inline operator T() const { return mValue; }
     constexpr inline BitFlags& operator|=(T rhs) { mValue |= rhs; return *this; }
     constexpr inline BitFlags& operator&=(T rhs) { mValue &= rhs; return *this; }
     constexpr inline bool test(T rhs) const { return (mValue & rhs) == rhs; }
     constexpr inline void set(T rhs) { mValue |= rhs; }
     constexpr inline void clear(T rhs) { mValue &= ~rhs; }

 private:
     T mValue;
 };
#endif //#define _BIT_FLAGS_H_

Có thể sử dụng:

#include <cstdint>
#include <BitFlags.h>
void main()
{
    enum class Options : uint32_t
    { 
          NoOption = 0 << 0
        , Option1  = 1 << 0
        , Option2  = 1 << 1
        , Option3  = 1 << 2
        , Option4  = 1 << 3
    };

    const uint32_t Option1 = 1 << 0;
    const uint32_t Option2 = 1 << 1;
    const uint32_t Option3 = 1 << 2;
    const uint32_t Option4 = 1 << 3;

   //Enum BitFlags
    BitFlags<Options> optionsEnum(Options::NoOption);
    optionsEnum.set(Options::Option1 | Options::Option3);

   //Standard integer BitFlags
    BitFlags<uint32_t> optionsUint32(0);
    optionsUint32.set(Option1 | Option3); 

    return 0;
}

3

@Xaqq đã cung cấp một cách thực sự an toàn cho loại cờ enum ở đây bởi một flag_setlớp.

Tôi đã xuất bản mã trong GitHub , cách sử dụng như sau:

#include "flag_set.hpp"

enum class AnimalFlags : uint8_t {
    HAS_CLAWS,
    CAN_FLY,
    EATS_FISH,
    ENDANGERED,
    _
};

int main()
{
    flag_set<AnimalFlags> seahawkFlags(AnimalFlags::HAS_CLAWS
                                       | AnimalFlags::EATS_FISH
                                       | AnimalFlags::ENDANGERED);

    if (seahawkFlags & AnimalFlags::ENDANGERED)
        cout << "Seahawk is endangered";
}

2

Bạn đang nhầm lẫn các đối tượng và bộ sưu tập của các đối tượng. Cụ thể, bạn đang nhầm lẫn cờ nhị phân với bộ cờ nhị phân. Một giải pháp thích hợp sẽ như thế này:

// These are individual flags
enum AnimalFlag // Flag, not Flags
{
    HasClaws = 0,
    CanFly,
    EatsFish,
    Endangered
};

class AnimalFlagSet
{
    int m_Flags;

  public:

    AnimalFlagSet() : m_Flags(0) { }

    void Set( AnimalFlag flag ) { m_Flags |= (1 << flag); }

    void Clear( AnimalFlag flag ) { m_Flags &= ~ (1 << flag); }

    bool Get( AnimalFlag flag ) const { return (m_Flags >> flag) & 1; }

};

2

Đây là giải pháp của tôi mà không cần bất kỳ quá tải hoặc đúc:

namespace EFoobar
{
    enum
    {
        FB_A    = 0x1,
        FB_B    = 0x2,
        FB_C    = 0x4,
    };
    typedef long Flags;
}

void Foobar(EFoobar::Flags flags)
{
    if (flags & EFoobar::FB_A)
        // do sth
        ;
    if (flags & EFoobar::FB_B)
        // do sth
        ;
}

void ExampleUsage()
{
    Foobar(EFoobar::FB_A | EFoobar::FB_B);
    EFoobar::Flags otherflags = 0;
    otherflags|= EFoobar::FB_B;
    otherflags&= ~EFoobar::FB_B;
    Foobar(otherflags);
}

Tôi nghĩ rằng nó ổn, bởi vì chúng tôi xác định (không gõ mạnh) enums và ints nào.

Cũng như một ghi chú bên (dài hơn), nếu bạn

  • muốn sử dụng enums đánh máy mạnh và
  • không cần một chút nặng nề với cờ của bạn
  • hiệu suất không phải là một vấn đề

Tôi sẽ đưa ra điều này:

#include <set>

enum class EFoobarFlags
{
    FB_A = 1,
    FB_B,
    FB_C,
};

void Foobar(const std::set<EFoobarFlags>& flags)
{
    if (flags.find(EFoobarFlags::FB_A) != flags.end())
        // do sth
        ;
    if (flags.find(EFoobarFlags::FB_B) != flags.end())
        // do sth
        ;
}

void ExampleUsage()
{
    Foobar({EFoobarFlags::FB_A, EFoobarFlags::FB_B});
    std::set<EFoobarFlags> otherflags{};
    otherflags.insert(EFoobarFlags::FB_B);
    otherflags.erase(EFoobarFlags::FB_B);
    Foobar(otherflags);
}

sử dụng danh sách khởi tạo C ++ 11 và enum class .


Nhân tiện, tôi không muốn giới thiệu enum cho cờ. Lý do đơn giản: kết hợp các cờ không phải là yếu tố của enum một lần nữa. Vì vậy, điều này có vẻ khá không phù hợp. Ngoài ra, tôi sẽ sử dụng using Flags = unsigned longbên trong một không gian tên hoặc cấu trúc có chứa các giá trị cờ như /*static*/ const Flags XY = 0x01vậy.
yau

1

Như trên (Kai) hoặc làm như sau. Thực sự enum là "En enations", những gì bạn muốn làm là có một bộ, do đó bạn thực sự nên sử dụng stl :: set

enum AnimalFlags
{
    HasClaws = 1,
    CanFly =2,
    EatsFish = 4,
    Endangered = 8
};

int main(void)
{
    AnimalFlags seahawk;
    //seahawk= CanFly | EatsFish | Endangered;
    seahawk= static_cast<AnimalFlags>(CanFly | EatsFish | Endangered);
}

1

Có lẽ giống như NS_OPTIONS của Objective-C.

#define ENUM(T1, T2) \
enum class T1 : T2; \
inline T1 operator~ (T1 a) { return (T1)~(int)a; } \
inline T1 operator| (T1 a, T1 b) { return static_cast<T1>((static_cast<T2>(a) | static_cast<T2>(b))); } \
inline T1 operator& (T1 a, T1 b) { return static_cast<T1>((static_cast<T2>(a) & static_cast<T2>(b))); } \
inline T1 operator^ (T1 a, T1 b) { return static_cast<T1>((static_cast<T2>(a) ^ static_cast<T2>(b))); } \
inline T1& operator|= (T1& a, T1 b) { return reinterpret_cast<T1&>((reinterpret_cast<T2&>(a) |= static_cast<T2>(b))); } \
inline T1& operator&= (T1& a, T1 b) { return reinterpret_cast<T1&>((reinterpret_cast<T2&>(a) &= static_cast<T2>(b))); } \
inline T1& operator^= (T1& a, T1 b) { return reinterpret_cast<T1&>((reinterpret_cast<T2&>(a) ^= static_cast<T2>(b))); } \
enum class T1 : T2

ENUM(Options, short) {
    FIRST  = 1 << 0,
    SECOND = 1 << 1,
    THIRD  = 1 << 2,
    FOURTH = 1 << 3
};

auto options = Options::FIRST | Options::SECOND;
options |= Options::THIRD;
if ((options & Options::SECOND) == Options::SECOND)
    cout << "Contains second option." << endl;
if ((options & Options::THIRD) == Options::THIRD)
    cout << "Contains third option." << endl;
return 0;

// Output:
// Contains second option. 
// Contains third option.

Bạn có thể giải thích tại sao câu trả lời của bạn là phù hợp nhất? Có một số câu trả lời khác đã trả lời câu hỏi này, vì vậy vui lòng bao gồm một số thông tin để phân biệt câu hỏi của bạn.
trevorp
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.