Enum liên tục C ++ 11


17

Có cách nào để kiểm tra C ++ 11 nếu enum liên tục không?

Nó hoàn toàn hợp lệ để đưa ra một giá trị enum không. Có thể có một tính năng như một đặc điểm loại trong C ++ 14, C ++ 17 hoặc có thể C ++ 20 để kiểm tra là enum có liên tục không? Điều này sẽ được sử dụng trong static_assert.

Một ví dụ nhỏ sau:

enum class Types_Discontinuous {
  A = 10,
  B = 1,
  C = 100
};

enum class Types_Continuous {
  A = 0,
  B = 1,
  C = 2
};

static_assert(SOME_TEST<Types_Discontinuous>::value, "Enum should be continuous"); // Fails
static_assert(SOME_TEST<Types_Continuous>::value, "Enum should be continuous");    // Passes

1
Có nghĩa là tiếp tục, rằng nó có thứ tự tăng dần hoặc có nghĩa là bắt đầu bằng 0 và sau đó +1 cho mỗi giá trị?
RoQuOTriX

5
Không có cách nào để liệt kê nhãn liệt kê vì vậy không thể thực hiện nó từ bên trong chương trình.
Một số lập trình viên anh chàng

1
Hấp dẫn. Tôi đang suy nghĩ về các dòng lập trình mẫu dọc theo các cách bạn có thể có được một trình biên dịch để tính toán một giai thừa. Bạn sẽ khởi động mọi thứ với hai giới hạn A và C và các chức năng mẫu kiểm tra thông qua SFINAE về sự hiện diện hay nói cách khác về tất cả các giá trị giữa chúng trong enum. Đáng buồn là tôi có một công việc ban ngày vì vậy không thể cố gắng viết ra điều này, mặc dù tôi sẽ đưa ra câu trả lời dựa trên phương pháp này. Tôi khá chắc chắn rằng ai đó như @barry hoặc @sehe có thể làm điều đó.
Bathsheba

1
@RoQuOTriX Bạn sẽ khớp giá trị với nhãn như thế nào? Và làm thế nào bạn sẽ kiểm tra thứ tự của các nhãn? Và làm thế nào nó có thể được thực hiện tại thời gian biên dịch (cần thiết cho static_assert)? Ngay cả khi bạn không thể tạo ra một "giải pháp đẹp", dù sao đi nữa, xin vui lòng viết câu trả lời vì tôi rất tò mò làm thế nào nó có thể được thực hiện theo cách chung chung.
Một số lập trình viên anh chàng

1
@Someprogrammerdude những gì bạn mô tả là "giải pháp đẹp" hoặc tốt. Ý tôi là giải pháp kiểm tra "dễ dàng" mà bạn sẽ phải viết lại cho mỗi enum và chúa phù hộ, tôi hy vọng không ai làm điều đó
RoQuOTriX

Câu trả lời:


7

Đối với một số enums, bạn có thể hack theo cách này bằng thư viện Magic Enum . Ví dụ:

#include "magic_enum.hpp"

template <typename Enum>
constexpr bool is_continuous(Enum = Enum{}) {
    // make sure we're actually testing an enum
    if constexpr (!std::is_enum_v<Enum>)
        return false;
    else {
        // get a sorted list of values in the enum
        const auto values = magic_enum::enum_values<Enum>();
        if (std::size(values) == 0)
            return true;

        // for every value, either it's the same as the last one or it's one larger
        auto prev = values[0];
        for (auto x : values) {
            auto next = static_cast<Enum>(magic_enum::enum_integer(prev) + 1);
            if (x != prev && x != next)
                return false;
            else
                prev = x;
        }
        return true;
    }
}

Lưu ý rằng đây thực sự là, như tên thư viện ngụ ý, "ma thuật" - chức năng thư viện trên một số bản hack dành riêng cho trình biên dịch. Vì vậy, nó không thực sự đáp ứng yêu cầu của bạn về "C ++ thuần túy", nhưng có lẽ tốt như chúng ta có thể nhận được cho đến khi chúng ta có các phương tiện phản ánh trong ngôn ngữ.


Nó thực sự là ma thuật nhưng điều này sẽ giải quyết tình huống của tôi tốt nhất.
Bart

7

Điều này là không thể trong C ++ thuần túy, bởi vì không có cách nào để liệt kê các giá trị enum hoặc khám phá số lượng các giá trị và các giá trị tối thiểu và tối đa. Nhưng bạn có thể thử sử dụng sự trợ giúp của trình biên dịch để thực hiện một cái gì đó gần với những gì bạn muốn. Ví dụ: trong gcc có thể thực thi lỗi biên dịch nếu một switchcâu lệnh không xử lý tất cả các giá trị của enum:

enum class my_enum {
    A = 0,
    B = 1,
    C = 2
};

#pragma GCC diagnostic push
#if __GNUC__ < 5
#pragma GCC diagnostic error "-Wswitch"
#else
#pragma GCC diagnostic error "-Wswitch-enum"
#endif

constexpr bool is_my_enum_continuous(my_enum t = my_enum())
{
    // Check that we know all enum values. Effectively works as a static assert.
    switch (t)
    {
    // Intentionally no default case.
    // The compiler will give an error if not all enum values are listed below.
    case my_enum::A:
    case my_enum::B:
    case my_enum::C:
        break;
    }

    // Check that the enum is continuous
    auto [min, max] = std::minmax({my_enum::A, my_enum::B, my_enum::C});
    return static_cast< int >(min) == 0 && static_cast< int >(max) == 2;
}

#pragma GCC diagnostic pop

Rõ ràng, điều này là chuyên biệt cho một enum nhất định, nhưng định nghĩa về các chức năng như vậy có thể được tự động hóa với bộ tiền xử lý.


Nếu tôi hiểu chính xác, điều này vẫn sẽ yêu cầu viết ra tất cả các giá trị enum trong công tắc và danh sách cho minmax. Hiện tại tôi có nhiều enum nên thực sự có thể nhưng không được ưu tiên cho tình huống của tôi.
Bart

1

Tôi muốn thấy một câu trả lời về điều này. Tôi cũng đang cần nó.

Thật không may, tôi không nghĩ rằng điều này là có thể bằng cách sử dụng các tiện ích hiện có. Nếu bạn muốn triển khai một đặc điểm loại này, bạn cần hỗ trợ từ trình biên dịch của mình, vì vậy việc viết một mẫu cho nó không có vẻ khả thi.

Tôi đã mở rộng phép liệt kê với một thẻ cụ thể để chỉ ra nó liền kề và ngay lập tức cung cấp cho bạn kích thước: enum class constructor c ++, làm thế nào để vượt qua giá trị cụ thể?

Ngoài ra, bạn có thể viết đặc điểm của riêng bạn:

 template<T> struct IsContiguous : std::false_type {};

Điều này cần phải được chuyên môn bất cứ khi nào bạn xác định một enum liền kề nơi bạn muốn sử dụng này. Thật không may, điều này đòi hỏi một số bảo trì và sự chú ý nếu enum bị thay đổi.


1
Bạn có thể viết trình kiểm tra mã, kiểm tra trong khi biên dịch, nếu loại được đặt chính xác
RoQuOTriX

Vâng, thực sự. Nếu bạn có khả năng viết nó.
JVApen

1

Tất cả enum là liên tục. 0 luôn được cho phép; giá trị cao nhất được phép là điều tra viên cao nhất được làm tròn đến tiếp theo 1<<N -1(tất cả các bit một) và tất cả các giá trị ở giữa cũng được cho phép. ([dcl.enum] 9.7.1 / 5). Nếu có các liệt kê âm được xác định, giá trị thấp nhất được phép cũng được xác định tương tự bằng cách làm tròn số liệt kê thấp nhất.

Các liệt kê được định nghĩa trong các enumbiểu thức không đổi có giá trị trong phạm vi và loại chính xác, nhưng bạn có thể xác định các hằng số bổ sung bên ngoài enumcó các thuộc tính giống nhau:

constexpr enum class Types_Discontinuous = static_cast<Types_Discontinuous>(2)


2
Mặc dù bạn đã đúng, nhưng rõ ràng từ OP rằng chúng tôi muốn biết điều này cho các giá trị được xác định. (PS: phiếu bầu không phải của tôi)
JVApen

1
@JVApen: Đó là vấn đề chính xác. "Các giá trị được xác định" không phải là một thuộc tính của kiểu enum. Tiêu chuẩn là rõ ràng các giá trị của enum là gì.
MSalters
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.