ฉันจะส่งออกค่าของคลาส enum ใน C ++ 11 ได้อย่างไร


101

ฉันจะส่งออกค่าของenum classใน C ++ 11 ได้อย่างไร ใน C ++ 03 จะเป็นเช่นนี้:

#include <iostream>

using namespace std;

enum A {
  a = 1,
  b = 69,
  c= 666
};

int main () {
  A a = A::c;
  cout << a << endl;
}

ใน c ++ 0x รหัสนี้ไม่ได้รวบรวม

#include <iostream>

using namespace std;

enum class A {
  a = 1,
  b = 69,
  c= 666
};

int main () {
  A a = A::c;
  cout << a << endl;
}


prog.cpp:13:11: error: cannot bind 'std::ostream' lvalue to 'std::basic_ostream<char>&&'
/usr/lib/gcc/i686-pc-linux-gnu/4.5.1/../../../../include/c++/4.5.1/ostream:579:5: error:   initializing argument 1 of 'std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char, _Traits = std::char_traits<char>, _Tp = A]'

รวบรวมที่Ideone.com


1
ทำไมคุณถึงพยายามแสดงผล enum? คลาส enum ใช้เพื่อไม่ผสมค่า enum กับการแทนค่า int
RiaD

คำตอบ:


126

ไม่เหมือนกับการแจงนับที่ไม่ได้กำหนดขอบเขตการแจงนับตามขอบเขตจะไม่สามารถแปลงเป็นค่าจำนวนเต็มได้โดยปริยาย คุณจำเป็นต้องชัดเจนแปลงเป็นจำนวนเต็มโดยใช้หล่อ:

std::cout << static_cast<std::underlying_type<A>::type>(a) << std::endl;

คุณอาจต้องการห่อหุ้มลอจิกลงในเทมเพลตฟังก์ชัน:

template <typename Enumeration>
auto as_integer(Enumeration const value)
    -> typename std::underlying_type<Enumeration>::type
{
    return static_cast<typename std::underlying_type<Enumeration>::type>(value);
}

ใช้เป็น:

std::cout << as_integer(a) << std::endl;

3
มีเหตุผลที่ใช้ไวยากรณ์ประเภทผลตอบแทนต่อท้ายหรือไม่?
Nicol Bolas

3
@NicolBolas: ฉันคัดลอกas_integerมาจากไลบรารีโอเพนซอร์ส CxxReflect (ดูenumeration.hpp ) ไลบรารีใช้ชนิดการส่งคืนต่อท้ายอย่างสม่ำเสมอทุกที่ เพื่อความสม่ำเสมอ.
James McNellis

11
แม้ว่าจะช้าไป 2 ปี แต่ในกรณีที่มีคนอื่นเห็นคำถามนี้คุณสามารถใช้วิธีเทคนิคการแคสต์ด้านบนและเรียก "static_cast <int> (value)" เพื่อรับจำนวนเต็มหรือ "static_cast <A> (intValue)" เป็น รับค่า enum เพียงจำไว้ว่าการเปลี่ยนจาก int เป็น enum หรือ enum เป็น enum อาจทำให้เกิดปัญหาได้และโดยทั่วไปมักเป็นสัญญาณของข้อบกพร่องในการออกแบบ
Benjamin Danger Johnson

4
int (ค่า) และ A (intValue) ยังใช้งานได้โดยไม่มีวงเล็บมุมที่น่าเกลียด
Grault

4
as_integerสามารถกำหนดให้เป็นconstexprเพื่อให้สามารถใช้ในบริบทที่ต้องการนิพจน์คงที่
Nawaz

40
#include <iostream>
#include <type_traits>

using namespace std;

enum class A {
  a = 1,
  b = 69,
  c= 666
};

std::ostream& operator << (std::ostream& os, const A& obj)
{
   os << static_cast<std::underlying_type<A>::type>(obj);
   return os;
}

int main () {
  A a = A::c;
  cout << a << endl;
}

ผมคัดลอกตัวอย่างนี้คำต่อคำและเรียบเรียงเป็นg++ -std=c++0x enum.cppแต่ฉันได้รับพวงของข้อผิดพลาดของคอมไพเลอร์ -> pastebin.com/JAtLXan9 ฉันยังไม่สามารถรับตัวอย่างจาก @ james-mcnellis เพื่อรวบรวมได้
Dennis

4
@ Dennis underlying_typeมีเฉพาะใน C ++ 11
Deqing

23

เป็นไปได้ที่จะรับตัวอย่างที่สองของคุณ (เช่นตัวอย่างที่ใช้ขอบเขตขอบเขต) เพื่อทำงานโดยใช้ไวยากรณ์เดียวกันกับ enums ที่ไม่ได้กำหนดขอบเขต นอกจากนี้โซลูชันยังเป็นแบบทั่วไปและจะใช้ได้กับ enums ที่กำหนดขอบเขตทั้งหมดเทียบกับการเขียนโค้ดสำหรับแต่ละขอบเขตที่กำหนด (ดังแสดงในคำตอบที่ให้ไว้โดย@ForEveR )

วิธีแก้ปัญหาคือการเขียนoperator<<ฟังก์ชันทั่วไปซึ่งจะใช้ได้กับ enum ที่กำหนดขอบเขตใด ๆ โซลูชันนี้ใช้SFINAEผ่านstd::enable_ifและมีดังต่อไปนี้

#include <iostream>
#include <type_traits>

// Scoped enum
enum class Color
{
    Red,
    Green,
    Blue
};

// Unscoped enum
enum Orientation
{
    Horizontal,
    Vertical
};

// Another scoped enum
enum class ExecStatus
{
    Idle,
    Started,
    Running
};

template<typename T>
std::ostream& operator<<(typename std::enable_if<std::is_enum<T>::value, std::ostream>::type& stream, const T& e)
{
    return stream << static_cast<typename std::underlying_type<T>::type>(e);
}

int main()
{
    std::cout << Color::Blue << "\n";
    std::cout << Vertical << "\n";
    std::cout << ExecStatus::Running << "\n";
    return 0;
}

คุณจำเป็นต้องมีก่อนtypename std::underlying_type<T>::type
uckelman

@uckelman คุณถูกต้องจริงๆ ขอบคุณสำหรับการอัปเดตคำตอบของฉัน
James Adkison

นี้ทำงานให้ฉันภายใต้เสียงดังกราว แต่ภายใต้ GCC 4.9.2 การแก้ปัญหานี้ล้มเหลวเมื่อผูกมัด << error: cannot bind ‘std::basic_ostream<char>’ lvalue to ‘std::basic_ostream<char>&&’ร่วมกันกับข้อผิดพลาด สิ่งนี้ดูเหมือนจะเป็นเพราะเมื่อสตรีมเป็นแบบชั่วคราว ADL จะล้มเหลวและเทมเพลตด้านบนไม่สามารถทำได้ เคล็ดลับใด ๆ
ofloveandhate

@ofloveandhate คุณช่วยให้ลิงค์ไปยังตัวอย่างที่ทำให้เกิดปัญหาได้หรือไม่? ฉันทดสอบโค้ดข้างต้นใน gcc 4.9.2 โดยไม่มีปัญหาใด ๆ และมีการเปลี่ยนแปลงเพียงเล็กน้อยฉันแปลง 3 coutคำสั่งเป็นcoutคำสั่งเดียวโดยผูกตัว<<ดำเนินการเข้าด้วยกัน ดูที่นี่
James Adkison

ให้ฉันแก้ไขคำพูดของฉัน ฉันพยายามพิมพ์คลาส enum ที่มีอยู่ในคลาสจากนอกคลาสนั้น โค้ดด้านบนใช้ได้กับคลาส enum ที่ไม่มีอยู่ในคลาสนั้น ๆ
ofloveandhate

10

(ฉันยังไม่ได้รับอนุญาตให้แสดงความคิดเห็น) ฉันขอแนะนำการปรับปรุงต่อไปนี้สำหรับคำตอบที่ยอดเยี่ยมของ James McNellis:

template <typename Enumeration>
constexpr auto as_integer(Enumeration const value)
    -> typename std::underlying_type<Enumeration>::type
{
    static_assert(std::is_enum<Enumeration>::value, "parameter is not of type enum or enum class");
    return static_cast<typename std::underlying_type<Enumeration>::type>(value);
}

ด้วย

  • constexpr: อนุญาตให้ฉันใช้ค่าสมาชิก enum เป็นขนาดอาร์เรย์คอมไพล์ไทม์
  • static_assert+ is_enum: เพื่อ 'ให้แน่ใจ' เวลาคอมไพล์ว่าฟังก์ชันทำ sth ด้วยการแจงนับเท่านั้นตามที่แนะนำ

ฉันถามตัวเองว่า: ทำไมฉันถึงต้องใช้enum classเมื่อฉันต้องการกำหนดค่าตัวเลขให้กับสมาชิก enum ของฉัน! พิจารณาความพยายามในการแปลง

บางทีฉันอาจจะกลับไปenumเป็นธรรมดาตามที่แนะนำไว้ที่นี่: จะใช้ enums เป็นแฟล็กใน C ++ ได้อย่างไร?


อีกหนึ่งรสชาติ (ดีกว่า) ของมันโดยไม่มี static_assert ตามคำแนะนำของ @TobySpeight:

template <typename Enumeration>
constexpr std::enable_if_t<std::is_enum<Enumeration>::value,
std::underlying_type_t<Enumeration>> as_number(const Enumeration value)
{
    return static_cast<std::underlying_type_t<Enumeration>>(value);
}

มีประเภทTที่std::underlying_type<T>::typeมีอยู่ แต่std::is_enum<T>::valueเป็นเท็จหรือไม่? ถ้าไม่เช่นนั้นstatic_assertจะไม่เพิ่มมูลค่า
Toby Speight

1
ฉันไม่ได้ทดสอบกับคอมไพเลอร์ทั้งหมด แต่ @TobySpeight คุณน่าจะใช่ msvc2013 ดูเหมือนจะพ่นข้อความแสดงข้อผิดพลาดที่เข้าใจได้โดยแนะนำการติดต่อแบบ 1 ต่อ 1 ระหว่าง underlying_type_t ที่มีอยู่และประเภทที่เป็น enum และ static_assert ไม่ได้ทำงานด้วยซ้ำ แต่: การอ้างอิงบอกว่าพฤติกรรมของ underlying_type ไม่ได้กำหนดไว้หากไม่ได้ระบุประเภท enum ที่สมบูรณ์ ดังนั้น static_assert จึงเป็นเพียงความหวังที่จะได้รับข้อความที่เข้าใจได้สูงสุดในกรณีนี้ อาจมีความเป็นไปได้ที่จะบังคับให้ประมวลผลเร็วที่สุด / เร็วที่สุด?
เหยา

อ่าใช่คุณพูดถูกที่ไม่ได้กำหนดถ้าEnumerationไม่ใช่ประเภท enum ที่สมบูรณ์ ในกรณีนี้อาจจะสายเกินไปแล้วเนื่องจากใช้ในประเภทการส่งคืน บางทีเราอาจระบุstd::enable_if<std::is_enum<Enumeration>::value, std::underlying_type<Enumeration>::type>เป็นประเภทผลตอบแทน? แน่นอนว่ามันง่ายกว่ามาก (และข้อความแสดงข้อผิดพลาดก็ชัดเจนขึ้นมาก) หากคุณมีคอมไพเลอร์ที่รองรับ Concepts ...
Toby Speight

8

หากต้องการเขียนให้ง่ายขึ้น

enum class Color
{
    Red = 1,
    Green = 11,
    Blue = 111
};

int value = static_cast<int>(Color::Blue); // 111

สิ่งนี้จะใช้ไม่ได้เมื่อ enum ระบุประเภทพื้นฐานไว้อย่างชัดเจน
James

3

การติดตามทำงานให้ฉันใน C ++ 11:

template <typename Enum>
constexpr typename std::enable_if<std::is_enum<Enum>::value,
                                  typename std::underlying_type<Enum>::type>::type
to_integral(Enum const& value) {
    return static_cast<typename std::underlying_type<Enum>::type>(value);
}

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