enum C ++ ต่อเนื่อง 11


17

มีวิธีการเช็คอินใน C ++ 11 ไหมถ้า enum ต่อเนื่องหรือไม่?

มันถูกต้องอย่างสมบูรณ์เพื่อให้ค่า enum ที่ไม่ได้ อาจมีคุณสมบัติเช่นลักษณะประเภทใน C ++ 14, C ++ 17 หรืออาจ C ++ 20 เพื่อตรวจสอบว่า enum เป็นแบบต่อเนื่องหรือไม่ สิ่งนี้จะใช้ใน static_assert

ตัวอย่างเล็ก ๆ ดังนี้:

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
หมายความว่าดำเนินการต่อไปว่ามีลำดับขึ้นหรือหมายความว่าเริ่มต้นด้วยศูนย์แล้ว +1 สำหรับทุกค่าหรือไม่
RoQuOTriX

5
ไม่มีวิธีในการระบุป้ายกำกับการแจงนับดังนั้นจึงเป็นไปไม่ได้ที่จะทำจากภายในโปรแกรมเอง
โปรแกรมเมอร์บางคนเพื่อน

1
น่าสนใจ ฉันกำลังคิดตามบรรทัดของการเขียนโปรแกรมเทมเพลตตามวิธีที่คุณจะได้รับคอมไพเลอร์เพื่อคำนวณแฟคทอเรียล คุณต้องการสิ่งที่เตะออกด้วยสองขอบเขต A และ C และฟังก์ชั่นแม่แบบตรวจสอบผ่านทาง SFINAE enumสำหรับการแสดงตนหรือมิฉะนั้นทุกค่าระหว่างพวกเขาใน น่าเศร้าที่ฉันมีงานประจำวันจึงไม่สามารถลองเขียนออกมาได้แม้ว่าฉันจะถอนคำตอบจากวิธีนี้ ฉันค่อนข้างมั่นใจว่าบางคนเช่น @barry หรือ @sehe สามารถทำได้
Bathsheba

1
@RoQuOTriX คุณจะจับคู่ค่ากับป้ายกำกับอย่างไร และคุณจะตรวจสอบคำสั่งของฉลากได้อย่างไร และจะทำอย่างไรในเวลารวบรวม (ซึ่งจำเป็นสำหรับstatic_assert) แม้ว่าคุณจะไม่สามารถสร้าง "วิธีแก้ปัญหาที่สวยงาม" ได้โปรดเขียนคำตอบต่อไปเพราะฉันอยากรู้อยากเห็นมากว่ามันสามารถทำได้ในลักษณะทั่วไป
โปรแกรมเมอร์บางคนเพื่อน

1
@Someprogrammerdude สิ่งที่คุณอธิบายคือ "สวย" หรือทางออกที่ดี สิ่งที่ฉันหมายถึงคือวิธีการตรวจสอบ "ง่าย" ซึ่งคุณจะต้องเขียนใหม่สำหรับทุก enum และพระเจ้าอวยพรฉันหวังว่าไม่มีใครทำอย่างนั้น
RoQuOTriX

คำตอบ:


7

สำหรับจำนวนของenumคุณอาจจะแฮ็คของคุณผ่านทางนี้โดยใช้ห้องสมุดMagic Enum ตัวอย่างเช่น:

#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;
    }
}

โปรดทราบว่านี่เป็นจริงตามที่ชื่อไลบรารีหมายถึง "magic" - ฟังก์ชั่นของไลบรารีในจำนวนของแฮ็คเฉพาะคอมไพเลอร์ ด้วยเหตุนี้มันจึงไม่เป็นไปตามความต้องการของคุณสำหรับ "pure C ++" แต่อาจดีเท่าที่เราจะทำได้จนกว่าเราจะมีสิ่งอำนวยความสะดวกในการสะท้อนในภาษา


มันวิเศษจริงๆ แต่สิ่งนี้จะเหมาะกับสถานการณ์ของฉันมากที่สุด
บาร์ต

7

สิ่งนี้เป็นไปไม่ได้ใน pure C ++ เนื่องจากไม่มีวิธีระบุค่า enum หรือค้นหาจำนวนค่าและค่าต่ำสุดและค่าสูงสุด แต่คุณสามารถลองใช้ความช่วยเหลือของคอมไพเลอร์เพื่อนำสิ่งที่ใกล้เคียงกับสิ่งที่คุณต้องการ ตัวอย่างเช่นใน gcc เป็นไปได้ที่จะบังคับใช้ข้อผิดพลาดในการรวบรวมหากswitchคำสั่งไม่จัดการค่าทั้งหมดของ 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

เห็นได้ชัดว่านี่เป็นพิเศษสำหรับ enum ที่กำหนด แต่คำจำกัดความของฟังก์ชั่นดังกล่าวสามารถทำงานอัตโนมัติด้วยตัวประมวลผลล่วงหน้า


หากฉันเข้าใจอย่างถูกต้องสิ่งนี้จะต้องเขียนค่า enum ทั้งหมดในสวิตช์และรายการสำหรับ minmax ปัจจุบันฉันมีหลาย enums ดังนั้นจึงเป็นไปได้จริง ๆ แต่ไม่ต้องการสำหรับสถานการณ์ของฉัน
บาร์ต

1

ฉันชอบที่จะเห็นคำตอบเกี่ยวกับเรื่องนี้ ฉันต้องการมันเช่นกัน

น่าเสียดายที่ฉันไม่คิดว่าจะสามารถใช้ยูทิลิตี้ที่มีอยู่ได้ หากคุณต้องการใช้คุณลักษณะประเภทนี้คุณต้องได้รับการสนับสนุนจากคอมไพเลอร์ของคุณดังนั้นการเขียนเทมเพลตสำหรับมันจะไม่เป็นไปได้

ฉันได้ขยายการแจงนับด้วยแท็กเฉพาะเพื่อระบุว่าต่อเนื่องกันและให้ขนาดทันที: ตัวสร้างคลาส enum c ++, วิธีส่งค่าเฉพาะ

หรือคุณสามารถเขียนคุณลักษณะของคุณเอง:

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

สิ่งนี้จำเป็นต้องใช้เฉพาะเมื่อใดก็ตามที่คุณกำหนด enum ที่ต่อเนื่องกันซึ่งคุณต้องการใช้ น่าเสียดายที่ต้องมีการบำรุงรักษาและให้ความสนใจหากค่า Enum เปลี่ยนไป


1
คุณสามารถเขียนรหัสตรวจสอบซึ่งตรวจสอบในขณะที่รวบรวมหากประเภทที่ถูกต้องตั้งค่า
RoQuOTriX

ใช่แน่นอน. หากคุณมีความสามารถในการเขียน
JVApen

1

Enum ทั้งหมดเป็นแบบต่อเนื่อง 0 ได้รับอนุญาตเสมอ; ค่าสูงสุดที่อนุญาตคือตัวแจงนับสูงสุดที่ปัดเศษขึ้นเป็นถัดไป1<<N -1(บิตทั้งหมดหนึ่ง) และค่าทั้งหมดที่อยู่ระหว่างนั้นจะได้รับอนุญาตด้วย ([dcl.enum] 9.7.1 / 5) หากมีการกำหนดตัวแจงนับเป็นลบค่าต่ำสุดที่อนุญาตจะถูกกำหนดในทำนองเดียวกันโดยการปัดเศษตัวแจงนับต่ำสุด

ตัวแจงนับที่กำหนดไว้ในenumคือนิพจน์คงที่ที่มีค่าอยู่ในช่วงและประเภทที่ถูกต้อง แต่คุณสามารถกำหนดค่าคงที่เพิ่มเติมภายนอกenumซึ่งมีคุณสมบัติเดียวกัน:

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


2
แม้ว่าคุณจะถูกต้องก็เป็นที่ชัดเจนจาก OP ที่เราต้องการทราบสิ่งนี้สำหรับค่าที่กำหนด (PS: การลงคะแนนเสียงไม่ได้เป็นของฉัน)
JVApen

1
@JVApen: นั่นเป็นปัญหาที่แน่นอน "ค่าที่กำหนด" ไม่ใช่คุณสมบัติของชนิด enum เอง มาตรฐานชัดเจนว่าค่าของ enum คืออะไร
MSalters
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.