Cách sử dụng enums trong C ++


218

Giả sử chúng ta có một enumcái như sau:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};

Tôi muốn tạo một thể hiện của điều này enumvà khởi tạo nó với một giá trị phù hợp, vì vậy tôi làm:

Days day = Days.Saturday;

Bây giờ tôi muốn kiểm tra biến hoặc thể hiện của mình với một enumgiá trị hiện có , vì vậy tôi làm:

if (day == Days.Saturday)
{
    std::cout << "Ok its Saturday";
}

Cung cấp cho tôi một lỗi biên dịch:

lỗi: dự kiến ​​biểu thức chính trước '.' mã thông báo

Vì vậy, để rõ ràng, sự khác biệt giữa nói:

if (day == Days.Saturday) // Causes compilation error

if (day == Saturday)

?

Hai cái này thực sự đề cập đến cái gì, trong đó một cái là OK và một cái gây ra lỗi biên dịch?


4
tôi biết, tôi muốn biết tại sao nó lại cho tôi lỗi!
Rika

1
Thứ tư của nó ở đây. Bạn có quá nhiều lỗi cú pháp cho trình biên dịch C ++. Bắt đầu từ 'Enum'.
Tiö Tiib

1
@Hossein, bởi vì enums không cùng một cú pháp (và ngữ nghĩa) trong cả hai ngôn ngữ. Điều đầu tiên tôi làm sau khi gặp lỗi khi cố gắng sử dụng một tính năng trong ngôn ngữ mới là tìm kiếm cú pháp (hoặc nếu có thể) trong ngôn ngữ đó.
chris

@chris: Tôi biết, tôi cũng làm điều tương tự. Hy vọng tôi đã có câu trả lời. Tôi cũng đã cập nhật câu hỏi để rõ ràng hơn. Cảm ơn bạn bằng cách này;)
Rika

17
"Theo như tôi biết thì việc khai báo và sử dụng enums trong hai ngôn ngữ này là như nhau. ". Có vấn đề của bạn, ngay tại đó. C # không cùng ngôn ngữ với C ++. Đặc biệt, họ có cú pháp khác nhau cho enums.
Cướp

Câu trả lời:


350

Mã này sai:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};
Days day = Days.Saturday;
if (day == Days.Saturday)

Bởi vì Dayskhông phải là một phạm vi, cũng không phải đối tượng. Nó là một loại. Và các loại bản thân họ không có thành viên. Những gì bạn viết là tương đương với std::string.clear. std::stringlà một loại, vì vậy bạn không thể sử dụng .nó. Bạn sử dụng .trên một thể hiện của một lớp.

Thật không may, enums là huyền diệu và vì vậy sự tương tự dừng lại ở đó. Bởi vì với một lớp, bạn có thể làm std::string::clearđể lấy một con trỏ tới hàm thành viên, nhưng trong C ++ 03, Days::Sundaykhông hợp lệ. (Mà buồn). Điều này là do C ++ (phần nào) tương thích ngược với C và C không có không gian tên, do đó, việc liệt kê phải nằm trong không gian tên toàn cầu. Vì vậy, cú pháp chỉ đơn giản là:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};
Days day = Saturday;
if (day == Saturday)

May mắn thay, Mike Seymour quan sát rằng điều này đã được giải quyết trong C ++ 11. Thay đổi enumđể enum classvà nó được phạm vi riêng của mình; vì vậy Days::Sundaykhông chỉ hợp lệ, mà là cách duy nhất để truy cập Sunday. Những ngày hạnh phúc!


254
May mắn thay, khiếu nại của bạn đã được giải quyết trong C ++ 11. Thay đổi enumđể enum classvà nó được phạm vi riêng của mình; vì vậy Days::Sundaykhông chỉ hợp lệ, mà là cách duy nhất để truy cập Sunday. Những ngày hạnh phúc!
Mike Seymour

11
Phải yêu các thông báo lỗi C ++ ... họ chứng minh rằng ngôn ngữ là cồng kềnh để thậm chí cung cấp phản hồi tốt. Tôi coi đó là 'biểu thức chính' là một đối tượng hoặc một phạm vi hoặc một số thứ khác KHÔNG phải là một loại. Có lẽ Loại là một 'biểu thức phụ'. Và những gì một nhà phát triển C ++ có thể gọi là 'toán tử chấm', trình biên dịch C ++ chỉ có thể gọi một 'mã thông báo'. Khi tôi cảm thấy khó hiểu các thông báo lỗi, có một cái gì đó sai với ngôn ngữ tôi nghĩ.
Travis

4
@Travis: en.cppreference.com/w/cpp/lingu/ trên . Một biểu thức chính chỉ là điều đầu tiên trong một biểu thức, thường là tên hoặc biến hoặc bằng chữ. Đối với phần thứ hai, tôi không thấy sự khác biệt lớn giữa '.' tokendot operator, ngoài đó là mã thông báo và không phải là nhà điều hành, và nó hiển thị biểu tượng chính xác, thay vì tên.
Vịt Mooing

@Mike Seymour Tôi đã cố gắng truy cập enum mà không có toán tử phân giải phạm vi trên một loạt các trình biên dịch và có vẻ như nó hoạt động. Bạn đã nói từ C ++ 11, đó là cách duy nhất, vì một số lý do tôi chỉ có thể truy cập các giá trị enum dưới dạng toàn cầu, không cần ::
Zebrafish

1
@TitoneMaurice: Nếu bạn có enum, bạn không thể sử dụng phạm vi hoặc phạm vi toàn cầu ( ::Saturday). Nếu bạn có một enum class(đó là một điều rất khác), thì bạn phải sử dụng Days::Saturday.
Vịt Mooing

24

Điều này sẽ đủ để khai báo biến enum của bạn và so sánh nó:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};
Days day = Saturday;
if (day == Saturday) {
    std::cout << "Ok its Saturday";
}

Tại sao lại sai khi nói nếu (ngày == Days.Satudday)? chúng phải giống nhau, vậy tại sao trình biên dịch lại phàn nàn về nó?
Rika

1
@Hossein các giá trị được khai báo trong enum của bạn không hoạt động như các biến thành viên lớp hoặc cấu trúc. Đây không phải là cú pháp chính xác để sử dụng
nhà toán

2
@Hossein: vì Dayskhông phải là phạm vi, cũng không phải đối tượng. Nó là một loại. Và các loại bản thân họ không có thành viên. std::string.clearcũng không biên dịch vì lý do tương tự.
Vịt Mooing

8
@Hossein: Bởi vì đó không phải là cách thức hoạt động của C ++. Các liệt kê không được kiểm soát đưa các giá trị của chúng vào không gian tên xung quanh; những người trong phạm vi ( enum class, mới trong năm 2011) có phạm vi riêng của họ và được truy cập bằng toán tử phạm vi , Days::Saturday. Toán tử truy cập thành viên ( .) chỉ được sử dụng để truy cập các thành viên lớp.
Mike Seymour

@MooingDUck và MikeSeymour Các bạn có thể gửi câu trả lời của bạn dưới dạng câu trả lời không? bởi vì đó chính xác là những gì tôi đã có sau khi đưa ra câu hỏi này;)
Rika

22

Phần lớn điều này sẽ cung cấp cho bạn các lỗi biên dịch.

// note the lower case enum keyword
enum Days { Saturday, Sunday, Monday, Tuesday, Wednesday, Thursday, Friday };

Bây giờ, Saturday, Sunday, vv có thể được sử dụng như hằng trần cấp cao nhất, và Dayscó thể được sử dụng như một loại:

Days day = Saturday;   // Days.Saturday is an error

Và tương tự sau đó, để kiểm tra:

if (day == Saturday)
    // ...

Các enumgiá trị này giống như các hằng số trần - chúng không bị giới hạn - với một chút trợ giúp từ trình biên dịch: (trừ khi bạn đang sử dụng các lớp enum C ++ 11 ), chúng không được gói gọn như các thành viên đối tượng hoặc cấu trúc, và bạn không thể coi họ là thành viên của Days.

Bạn sẽ có những gì bạn đang tìm kiếm với C ++ 11 , giới thiệu enum class:

enum class Days
{
    SUNDAY,
    MONDAY,
    // ... etc.
}

// ...

if (day == Days::SUNDAY)
    // ...

Lưu ý rằng C ++ này hơi khác so với C theo một số cách, một là C yêu cầu sử dụng enumtừ khóa khi khai báo một biến:

// day declaration in C:
enum Days day = Saturday;

Tôi đã cập nhật câu hỏi, tôi nghĩ bây giờ nó rõ ràng hơn những gì tôi chính xác sau :) Nhân tiện cảm ơn :)
Rika

14

Bạn có thể sử dụng một mẹo để sử dụng phạm vi theo ý muốn, chỉ cần khai báo enum theo cách như sau:

struct Days 
{
   enum type
   {
      Saturday,Sunday,Tuesday,Wednesday,Thursday,Friday
   };
};

Days::type day = Days::Saturday;
if (day == Days::Saturday)

9

Thay vì sử dụng một loạt các câu lệnh if, enums cho vay tốt để chuyển đổi các câu lệnh

Tôi sử dụng một số kết hợp enum / switch trong trình xây dựng cấp độ mà tôi đang xây dựng cho trò chơi của mình.

EDIT: Một điều nữa, tôi thấy bạn muốn cú pháp tương tự;

if(day == Days.Saturday)
etc

Bạn có thể làm điều này trong C ++:

if(day == Days::Saturday)
etc

Đây là một ví dụ rất đơn giản:

EnumAppState.h

#ifndef ENUMAPPSTATE_H
#define ENUMAPPSTATE_H
enum eAppState
{
    STARTUP,
    EDIT,
    ZONECREATION,
    SHUTDOWN,
    NOCHANGE
};
#endif

Một sốfile.cpp

#include "EnumAppState.h"
eAppState state = eAppState::STARTUP;
switch(state)
{
case STARTUP:
    //Do stuff
    break;
case EDIT:
    //Do stuff
    break;
case ZONECREATION:
    //Do stuff
    break;
case SHUTDOWN:
    //Do stuff
    break;
case NOCHANGE:
    //Do stuff
    break;
}

Điều tuyệt vời ở đây là trình biên dịch sẽ cho bạn biết nếu bạn lỡ đặt trường hợp vào.
Chris

Bạn không nên sử dụng lớp enum trong trường hợp này?
Rika

1
enum chỉ là một kiểu dữ liệu trong C ++ Vì vậy, việc khai báo một enum như tôi đã làm ở trên trong một tệp .h, và sau đó bao gồm tệp đó trong bất kỳ tệp .cpp nào bạn muốn sử dụng sẽ cho phép bạn truy cập vào enum. Chỉ cần lưu ý rằng tôi đã quên thêm #include trong ví dụ .cpp của mình. Chỉnh sửa.
Dean Knight

Ngoài ra, tôi thấy một người khác nói rằng enum trong C ++ là toàn cầu. Theo kinh nghiệm của tôi, sử dụng enums theo cách tôi có ở trên, tôi chỉ có thể truy cập chúng khi tôi đã bao gồm .h. Vì vậy, điều này dường như cũng dừng truy cập toàn cầu, điều này luôn luôn tốt. EDIT: Có vẻ như tôi vô tình sử dụng enum theo cách C ++ 11 nếu tôi đọc đúng thứ ...
Dean Knight

9

Nếu bạn vẫn đang sử dụng C ++ 03 và muốn sử dụng enums, bạn nên sử dụng enums bên trong một không gian tên. Ví dụ:

namespace Daysofweek{
enum Days {Saturday, Sunday, Tuesday,Wednesday, Thursday, Friday};
}

Bạn có thể sử dụng enum bên ngoài không gian tên như,

Daysofweek::Days day = Daysofweek::Saturday;

if (day == Daysofweek::Saturday)
{
    std::cout<<"Ok its Saturday";
}

8

Bạn đang tìm kiếm các kiểu liệt kê được gõ mạnh , một tính năng có sẵn trong tiêu chuẩn C ++ 11 . Nó biến bảng liệt kê thành các lớp với các giá trị phạm vi.

Sử dụng ví dụ mã của riêng bạn, đó là:

  enum class Days {Saturday, Sunday, Tuesday,Wednesday, Thursday, Friday};
  Days day = Days::Saturday;

  if (day == Days::Saturday)  {
    cout << " Today is Saturday !" << endl;
  }
  //int day2 = Days::Sunday; // Error! invalid

Sử dụng ::làm người truy cập để liệt kê sẽ thất bại nếu nhắm mục tiêu tiêu chuẩn C ++ trước C ++ 11. Nhưng một số trình biên dịch cũ không hỗ trợ nó, cũng như một số IDE chỉ ghi đè tùy chọn này và đặt tiêu chuẩn C ++ cũ.

Nếu bạn đang sử dụng GCC, hãy bật C + 11 với -std = c ++ 11 hoặc -std = gnu11 .

Hãy hạnh phúc!


1
Bạn quên viết enum class Days { ....
Martin Hennings

Thật. sửa nó Cảm ơn.
Alex Byrth

7

Điều này không nên hoạt động trong C ++:

Days.Saturday

Ngày không phải là một phạm vi hoặc đối tượng có chứa các thành viên bạn có thể truy cập bằng toán tử dấu chấm. Cú pháp này chỉ là chủ nghĩa C # và không hợp pháp trong C ++.

Microsoft từ lâu đã duy trì một tiện ích mở rộng C ++ cho phép bạn truy cập các mã định danh bằng toán tử phạm vi:

enum E { A, B, C };

A;
E::B; // works with Microsoft's extension

Nhưng điều này là không chuẩn trước C ++ 11. Trong C ++ 03, các định danh được khai báo trong một enum chỉ tồn tại trong cùng phạm vi với chính kiểu enum.

A;
E::B; // error in C++03

C ++ 11 làm cho nó hợp pháp để đủ điều kiện nhận dạng enum với tên enum và cũng giới thiệu các lớp enum, tạo ra một phạm vi mới cho các định danh thay vì đặt chúng trong phạm vi xung quanh.

A;
E::B; // legal in C++11

enum class F { A, B, C };

A; // error
F::B;

4

Đáng buồn thay, các yếu tố của enum là "toàn cầu". Bạn truy cập chúng bằng cách làm day = Saturday. Điều đó có nghĩa là bạn không thể có enum A { a, b } ;enum B { b, a } ;vì họ đang xung đột.


2
Cho đến khi bạn sử dụng enum classtrong C ++ 11, đó là. Trước đó, bạn phải làm các lớp giả.
chris

Không biết C ++ 11. Tôi giả sử câu hỏi đề cập đến C ++. Có, sử dụng các lớp hoặc không gian tên sẽ thực hiện thủ thuật.
Grzegorz

@Grzegorz: tôi nghĩ rằng chris đang đề cập đến lớp enum mới được giới thiệu, cung cấp các enum được gõ mạnh.
Rika

@Hossein: Cảm ơn bạn đã chỉ ra. Tôi đã tìm thấy lời giải thích về lớp num và tôi biết Chris đang nói về cái gì. Cảm ơn rất nhiều.
Grzegorz

@Grzegorz: Tôi không có ý thiếu tôn trọng, chỉ nghĩ rằng tôi có thể giúp đỡ, xin lỗi vì bất kỳ sự hiểu lầm có thể xảy ra. Tôi một lần nữa Cảm ơn bạn đã dành thời gian và giúp tôi;)
Rika

4

Trong khi C ++ (không bao gồm C ++ 11) có enum, các giá trị trong chúng bị "rò rỉ" vào không gian tên toàn cầu.
Nếu bạn không muốn chúng bị rò rỉ (và KHÔNG CẦN sử dụng loại enum), hãy xem xét những điều sau:

class EnumName {  
   public:   
      static int EnumVal1;  
      (more definitions)  
};  
EnumName::EnumVal1 = {value};  
if ([your value] == EnumName::EnumVal1)  ...

3

Các enum trong C ++ giống như các số nguyên được che dấu bởi các tên bạn đặt cho chúng, khi bạn khai báo các giá trị enum của bạn (đây không phải là một định nghĩa chỉ là một gợi ý về cách thức hoạt động của nó).

Nhưng có hai lỗi trong mã của bạn:

  1. Đánh vần enumtất cả chữ thường
  2. Bạn không cần Days.trước thứ bảy.
  3. Nếu enum này được khai báo trong một lớp, sau đó sử dụng if (day == YourClass::Saturday){}

OP đã thay đổi chính tả / trường hợp 16 phút sau bài đăng đầu tiên ( phiên bản 1 thành phiên bản 2 ).
Peter Mortensen

1

Tôi nghĩ vấn đề gốc của bạn là việc sử dụng .thay vì ::, sẽ sử dụng không gian tên.

Thử:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};
Days day = Days::Saturday;
if(Days::Saturday == day)  // I like literals before variables :)
{
    std::cout<<"Ok its Saturday";
}

Điều này không hoạt động: để sử dụng Days::phạm vi như trong ví dụ của bạn, bạn phải xác định phép liệt kê với enum class Daysvà sử dụng phần mở rộng C ++ 03 + Microsoft hoặc C ++ 11.
Futal

@Futal, ở trên đã chạy với Borland C ++ Builder. Hương vị / Phiên bản của C ++ không có trong câu hỏi.
James Oravec

1
phiên bản Borland C ++ Builder của bạn phải sử dụng C ++ 11 hoặc mới hơn. Cả Gcc và Clang đều đưa ra lỗi hoặc cảnh báo nếu ví dụ của bạn được biên dịch bằng -std=c++98hoặc -std=c++03. Clang khá rõ ràng : warning: use of enumeration in a nested name specifier is a C++11 extension.
Futal

1

Nếu chúng ta muốn loại an toàn nghiêm ngặt và enum có phạm vi, sử dụng enum classlà tốt trong C ++ 11.

Nếu chúng tôi phải làm việc trong C ++ 98, chúng tôi có thể sử dụng lời khuyên được đưa ra bởi InitializeSahib, Sanđể kích hoạt enum phạm vi.

Nếu chúng tôi cũng muốn loại an toàn nghiêm ngặt, mã theo dõi có thể thực hiện bất cứ điều gì như thế enum.

#include <iostream>
class Color
{
public:
    static Color RED()
    {
        return Color(0);
    }
    static Color BLUE()
    {
        return Color(1);
    }
    bool operator==(const Color &rhs) const
    {
        return this->value == rhs.value;
    }
    bool operator!=(const Color &rhs) const
    {
        return !(*this == rhs);
    }

private:
    explicit Color(int value_) : value(value_) {}
    int value;
};

int main()
{
    Color color = Color::RED();
    if (color == Color::RED())
    {
        std::cout << "red" << std::endl;
    }
    return 0;
}

Mã được sửa đổi từ ví dụ Tháng lớp trong cuốn sách Hiệu quả C ++ thứ 3: Mục 18


-15

Trước hết, tạo 'E' trong enum, 'e' làm chữ thường.

Thứ hai, loại tên thả 'Ngày' trong 'Ngày. Ngày'.

Thứ ba ... mua cho mình một cuốn sách C ++ tốt.


5
Xin lỗi, bạn đã nhận được tất cả những phiếu bầu này (ý tôi là, câu trả lời không xứng đáng), nhưng điều đó không có nghĩa là bạn phải rời khỏi cộng đồng trong 6 năm. Hãy trở lại và tham gia với chúng tôi. Bạn có một cái gì đó để đóng góp quá. Hãy giúp đỡ Chia sẻ kiến ​​thức.
Gabriel Staples
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.