कैसे स्वचालित रूप से दृढ़ता से टाइप एनम को इंट में परिवर्तित करने के लिए?


165
#include <iostream>

struct a {
  enum LOCAL_A { A1, A2 };
};
enum class b { B1, B2 };

int foo(int input) { return input; }

int main(void) {
  std::cout << foo(a::A1) << std::endl;
  std::cout << foo(static_cast<int>(b::B2)) << std::endl;
}

वह a::LOCAL_Aहै जो दृढ़ता से टाइप की गई एनम हासिल करने की कोशिश कर रहा है, लेकिन एक छोटा सा अंतर है: सामान्य एनम को पूर्णांक प्रकार में परिवर्तित किया जा सकता है, जबकि दृढ़ता से टाइप किए गए एनम बिना कलाकारों के नहीं कर सकते हैं।

तो, क्या एक डाली के बिना पूर्ण रूप से टाइप किए गए एनम मूल्य को पूर्णांक प्रकार में बदलने का एक तरीका है? यदि हाँ, तो कैसे?

जवाबों:


134

कई समस्याओं को हल करने के उद्देश्य से मजबूत टाइप किए गए एनम और न केवल आपके प्रश्न में वर्णित समस्या को हल करने के लिए:

  1. इस प्रकार से सुरक्षा प्रदान करें, इस प्रकार इंटीग्रल प्रमोशन द्वारा पूर्णांक में निहित रूपांतरण को समाप्त किया जाए।
  2. अंतर्निहित प्रकार निर्दिष्ट करें।
  3. मजबूत स्कूपिंग प्रदान करें।

इस प्रकार, पूर्ण रूप से टाइप किए गए एनम को पूर्णांक, या यहां तक ​​कि इसके अंतर्निहित प्रकार में परिवर्तित करना असंभव है - यही विचार है। इसलिए आपको static_castरूपांतरण को स्पष्ट करने के लिए उपयोग करना होगा।

यदि आपकी एकमात्र समस्या स्कूपिंग है और आप वास्तव में पूर्णांकों के लिए अंतर्निहित पदोन्नति चाहते हैं, तो आप उस संरचना के दायरे के साथ दृढ़ता से टाइप किए गए एनम का उपयोग करना बेहतर समझते हैं, जो इसमें घोषित किया गया है।


2
C ++ रचनाकारों से 'हम बेहतर जानते हैं कि आप क्या करना चाहते हैं' इसका एक और अजीब उदाहरण है। परम्परागत (पुरानी शैली वाली) इनमों में अनुक्रमणिका में निहित रूपांतरण जैसे बिटकॉइन संचालन आदि का सहज उपयोग करने के बहुत सारे लाभ थे। नई शैली की एनमों में वास्तव में बहुत अच्छी बात थी, लेकिन ... आप केवल उस चीज का उपयोग नहीं कर सकते हैं (स्पष्ट के साथ भी) अंतर्निहित प्रकार विनिर्देश!)। तो अब आप या तो पुराने स्टाइल के इनमों को ट्रिक के साथ उपयोग करने के लिए मजबूर कर रहे हैं जैसे उन्हें स्ट्रक्चर में डालना या नए एनम के लिए सबसे जल्दी वर्कअराउंड बनाना जैसे कि std के आसपास अपना खुद का रैपर बनाना :: वेक्टर सिर्फ उस CAST चीज को दूर करने के लिए। कोई टिप्पणी नहीं
avtomaton

152

जैसा कि अन्य लोगों ने कहा है, आपके पास एक अंतर्निहित रूपांतरण नहीं हो सकता है, और वह है डिज़ाइन।

यदि आप चाहते हैं तो आप कलाकारों में अंतर्निहित प्रकार को निर्दिष्ट करने की आवश्यकता से बच सकते हैं।

template <typename E>
constexpr typename std::underlying_type<E>::type to_underlying(E e) noexcept {
    return static_cast<typename std::underlying_type<E>::type>(e);
}

std::cout << foo(to_underlying(b::B2)) << std::endl;

75

आर। मार्टिनो फर्नांडीस द्वारा दिए गए उत्तर का C ++ 14 संस्करण होगा:

#include <type_traits>

template <typename E>
constexpr auto to_underlying(E e) noexcept
{
    return static_cast<std::underlying_type_t<E>>(e);
}

पिछले उत्तर के साथ, यह किसी भी तरह के एनम और अंतर्निहित प्रकार के साथ काम करेगा। मैंने noexceptकीवर्ड जोड़ा है क्योंकि यह कभी भी अपवाद नहीं करेगा।


अपडेट
यह स्कॉट मेयर्स द्वारा प्रभावी आधुनिक सी ++ में भी दिखाई देता है । आइटम 10 देखें (यह पुस्तक की मेरी प्रति के भीतर आइटम के अंतिम पन्नों में विस्तृत है)।


18
#include <cstdlib>
#include <cstdio>
#include <cstdint>

#include <type_traits>

namespace utils
{

namespace details
{

template< typename E >
using enable_enum_t = typename std::enable_if< std::is_enum<E>::value, 
                                               typename std::underlying_type<E>::type 
                                             >::type;

}   // namespace details


template< typename E >
constexpr inline details::enable_enum_t<E> underlying_value( E e )noexcept
{
    return static_cast< typename std::underlying_type<E>::type >( e );
}   


template< typename E , typename T>
constexpr inline typename std::enable_if< std::is_enum<E>::value &&
                                          std::is_integral<T>::value, E
                                         >::type 
 to_enum( T value ) noexcept 
 {
     return static_cast<E>( value );
 }

} // namespace utils




int main()
{
    enum class E{ a = 1, b = 3, c = 5 };

    constexpr auto a = utils::underlying_value(E::a);
    constexpr E    b = utils::to_enum<E>(5);
    constexpr auto bv = utils::underlying_value(b);

    printf("a = %d, b = %d", a,bv);
    return 0;
}

3
यह टाइपिंग को कम नहीं करता है या कोड क्लीनर नहीं बनाता है और बड़ी परियोजनाओं में इस तरह के निहितार्थों को खोजने के लिए इसे कठिन बनाने के दुष्प्रभाव हैं। Static_cast इन निर्माणों की तुलना में विस्तृत परियोजना को खोजना आसान होगा।
अतुल कुमार

3
@AtulKumar स्थैतिक_कास्ट कैसे खोज रहा है जो कि to_enum की तुलना में आसान है?
जोहान गेर्ले

1
इस उत्तर के लिए कुछ स्पष्टीकरण और प्रलेखन की आवश्यकता है।
को ऑर्बिट

17

नहीं, कोई प्राकृतिक तरीका नहीं है

वास्तव में, enum classC ++ 11 में दृढ़ता से टाइप करने के पीछे एक प्रेरणा उनके मौन रूपांतरण को रोकना है int


खुर्शीद नॉर्मुरादोव के उत्तर पर एक नज़र डालें। यह 'प्राकृतिक तरीका' है और 'C ++ प्रोग्रामिंग लैंग्वेज (4th ed।)' के रूप में ज्यादा है। यह एक 'स्वचालित तरीके' में नहीं आता है, और इसके बारे में अच्छा है।
पापाअथोम

@PapaAtHome मुझे static_cast पर उस लाभ का बोध नहीं है। टाइपिंग या कोड क्लीननेस में ज्यादा बदलाव नहीं। यहाँ प्राकृतिक तरीका क्या है? एक समारोह वापसी मूल्य?
अतुल कुमार

1
@ user2876962, मेरे लिए, लाभ यह है कि यह स्वचालित या 'मौन' नहीं है, क्योंकि Iammilind इसे डालता है। यह त्रुटियों को खोजने के लिए अंतर को रोकता है। आप अभी भी एक कास्ट कर सकते हैं लेकिन आप इसके बारे में सोचने के लिए मजबूर हैं। इस तरह से आप जानते हैं कि आप क्या कर रहे हैं। मेरे लिए यह एक 'सुरक्षित कोडिंग' की आदत का हिस्सा है। मैं पसंद करता हूं कि कोई भी रूपांतरण स्वचालित रूप से नहीं किया जाता है वहाँ एक मौका है कि यह एक त्रुटि का परिचय दे सकता है। यदि आप मुझसे पूछें तो इस श्रेणी में आने वाले प्रकार के सिस्टम से संबंधित C ++ 11 में कुछ बदलाव होंगे।
पापाअटहोम

17

अंतर्निहित रूपांतरण की अनुपस्थिति का कारण (डिजाइन द्वारा) अन्य उत्तरों में दिया गया था।

मैं व्यक्तिगत रूप operator+से enum वर्गों से उनके अंतर्निहित प्रकार में रूपांतरण के लिए unary का उपयोग करता हूं :

template <typename T>
constexpr auto operator+(T e) noexcept
    -> std::enable_if_t<std::is_enum<T>::value, std::underlying_type_t<T>>
{
    return static_cast<std::underlying_type_t<T>>(e);
}

जो काफी कम "टाइपिंग ओवरहेड" देता है:

std::cout << foo(+b::B2) << std::endl;

जहां मैं वास्तव में एक मैक्रो का उपयोग करने के लिए enums बनाने के लिए और ऑपरेटर एक शॉट में कार्य करता है।

#define UNSIGNED_ENUM_CLASS(name, ...) enum class name : unsigned { __VA_ARGS__ };\
inline constexpr unsigned operator+ (name const val) { return static_cast<unsigned>(val); }

13

आशा है कि यह आपकी या किसी और की मदद करेगा

enum class EnumClass : int //set size for enum
{
    Zero, One, Two, Three, Four
};

union Union //This will allow us to convert
{
    EnumClass ec;
    int i;
};

int main()
{
using namespace std;

//convert from strongly typed enum to int

Union un2;
un2.ec = EnumClass::Three;

cout << "un2.i = " << un2.i << endl;

//convert from int to strongly typed enum
Union un;
un.i = 0; 

if(un.ec == EnumClass::Zero) cout << "True" << endl;

return 0;
}

33
इसे "टाइप पाइंटिंग" कहा जाता है और हालांकि कुछ संकलक द्वारा समर्थित पोर्टेबल नहीं है, जैसा कि C ++ मानक कहता है कि आप सेट करने के बाद un.i"सक्रिय सदस्य" हैं और आप केवल एक संघ के सक्रिय सदस्य को पढ़ सकते हैं।
जोनाथन वेकली

6
@JonathanWakely आप तकनीकी रूप से सही हैं, लेकिन मैंने कभी ऐसा कंपाइलर नहीं देखा, जहां यह मज़बूती से काम नहीं करता हो। इस तरह से सामान, अनाम संघों, और #pragma एक बार defacto मानकों हैं।
BigSandwich

5
कुछ का उपयोग क्यों करें जो मानक स्पष्ट रूप से मना करता है, जब एक साधारण कलाकार करेगा? यह सिर्फ गलत है।
पॉल ग्रूके

1
तकनीकी रूप से सही है या नहीं, मेरे लिए यह अधिक पठनीय है तो अन्य समाधान यहां मिलेंगे। और जो मेरे लिए अधिक महत्वपूर्ण है, इसका उपयोग न केवल क्रमबद्धता को हल करने के लिए किया जा सकता है, बल्कि आसानी से और पठनीय प्रारूप के साथ एनम वर्ग के deserialization।
मार्सिन वेनियोव्स्की

6
मुझे पूरी तरह से निराशा है कि ऐसे लोग हैं जो इस गन्दे अपरिभाषित व्यवहार को "सरल तरीके से अधिक पठनीय" मानते हैं static_cast
अंडरस्कोर_ड

13

संक्षिप्त उत्तर यह है कि आप उपरोक्त पदों को इंगित नहीं कर सकते। लेकिन मेरे मामले के लिए, मैं बस नाम स्थान को अव्यवस्थित नहीं करना चाहता था, लेकिन अभी भी अंतर्निहित रूपांतरण हैं, इसलिए मैंने अभी किया:

#include <iostream>

using namespace std;

namespace Foo {
   enum Foo { bar, baz };
}

int main() {
   cout << Foo::bar << endl; // 0
   cout << Foo::baz << endl; // 1
   return 0;
}

नाम-प्रकार की सुरक्षा एक प्रकार की सुरक्षा को जोड़ती है जबकि मुझे अंतर्निहित प्रकार के लिए किसी भी एनम मान को स्थिर करने की आवश्यकता नहीं है।


3
यह कोई प्रकार की सुरक्षा नहीं जोड़ता है (वास्तव में, आपने बस प्रकार की सुरक्षा को हटा दिया है) - यह केवल नाम स्कूपिंग जोड़ता है।
ऑर्बिट

@LightnessRacesinOrbit हाँ मैं सहमत हूँ। मैंने झूठ बोला। तकनीकी रूप से, सटीक होने के लिए, प्रकार केवल एक नाम स्थान / दायरे के नीचे है और पूरी तरह से योग्य है Foo::Foo। सदस्यों के रूप में पहुँचा जा सकता है Foo::barऔर Foo::bazअंतर्निहित रूप से डाली जा सकती है (और बहुत अधिक सुरक्षा नहीं)। यह शायद बेहतर है कि लगभग हमेशा एनम वर्गों का उपयोग करें खासकर यदि एक नई परियोजना शुरू करना।
solstice333

6

यह मूल के साथ असंभव लगता है enum class, लेकिन शायद आप एक के enum classसाथ मजाक कर सकते हैं class:

इस मामले में,

enum class b
{
    B1,
    B2
};

इसके बराबर होगा:

class b {
 private:
  int underlying;
 public:
  static constexpr int B1 = 0;
  static constexpr int B2 = 1;
  b(int v) : underlying(v) {}
  operator int() {
      return underlying;
  }
};

यह ज्यादातर मूल के बराबर है enum class। आप सीधे b::B1रिटर्न प्रकार के साथ एक फ़ंक्शन के लिए वापस आ सकते हैं b। आप इसके switch caseसाथ कर सकते हैं , आदि।

और इस उदाहरण की भावना में आप enum classसिंटैक्स द्वारा परिभाषित किसी भी संभावित वस्तु को सामान्य और नकली करने के लिए टेम्पलेट का उपयोग कर सकते हैं (संभवतः अन्य चीजों के साथ) ।


लेकिन B1 और B2 को कक्षा के बाहर परिभाषित किया जाना चाहिए ... या यह मामले के लिए अनुपयोगी है - शीर्ष लेख .h <- class b - main.cpp <---- myvector.push_back (B1)
Fl0

"स्थिर स्थिरांक int" के बजाय "स्थिर कॉन्स्टैक्स b" नहीं होना चाहिए? अन्यथा, b :: B1 केवल एक प्रकार का कोई अंतर नहीं है, जो किसी भी प्रकार का हो।
कुछ लड़के

4

जैसा कि कई ने कहा, ओवरहेड्स और बहुत अधिक जटिलता को जोड़े बिना स्वचालित रूप से परिवर्तित करने का कोई तरीका नहीं है, लेकिन आप अपने टाइपिंग को थोड़ा कम कर सकते हैं और लंबोदा का उपयोग करके इसे बेहतर बना सकते हैं यदि कुछ कलाकारों को परिदृश्य में थोड़ा अधिक उपयोग किया जाएगा। यह फंक्शन ओवरहेड कॉल को थोड़ा जोड़ देगा, लेकिन कोड को लंबे स्टेटिक_कास्ट स्ट्रिंग्स की तुलना में अधिक पठनीय बना देगा जैसा कि नीचे देखा जा सकता है। यह उपयोगी प्रोजेक्ट वाइड नहीं हो सकता है, लेकिन केवल क्लास वाइड हो सकता है।

#include <bitset>
#include <vector>

enum class Flags { ......, Total };
std::bitset<static_cast<unsigned int>(Total)> MaskVar;
std::vector<Flags> NewFlags;

-----------
auto scui = [](Flags a){return static_cast<unsigned int>(a); };

for (auto const& it : NewFlags)
{
    switch (it)
    {
    case Flags::Horizontal:
        MaskVar.set(scui(Flags::Horizontal));
        MaskVar.reset(scui(Flags::Vertical)); break;
    case Flags::Vertical:
        MaskVar.set(scui(Flags::Vertical));
        MaskVar.reset(scui(Flags::Horizontal)); break;

   case Flags::LongText:
        MaskVar.set(scui(Flags::LongText));
        MaskVar.reset(scui(Flags::ShorTText)); break;
    case Flags::ShorTText:
        MaskVar.set(scui(Flags::ShorTText));
        MaskVar.reset(scui(Flags::LongText)); break;

    case Flags::ShowHeading:
        MaskVar.set(scui(Flags::ShowHeading));
        MaskVar.reset(scui(Flags::NoShowHeading)); break;
    case Flags::NoShowHeading:
        MaskVar.set(scui(Flags::NoShowHeading));
        MaskVar.reset(scui(Flags::ShowHeading)); break;

    default:
        break;
    }
}

2

C ++ कमेटी ने एक कदम आगे बढ़ाया (ग्लोबल नेमस्पेस से बाहर एनोम्स को छोड़ दिया) और पचास कदम पीछे (पूर्णांक के लिए कोई एनुम प्रकार क्षय नहीं)। अफसोस की बात है, enum classअगर आप किसी भी गैर-प्रतीकात्मक तरीके से एनम के मूल्य की आवश्यकता है, तो बस उपयोग करने योग्य नहीं है।

सबसे अच्छा उपाय यह है कि इसका उपयोग बिल्कुल न किया जाए, और इसके बजाय एक नाम स्थान या एक संरचना का उपयोग करके अपने आप को enum स्कोप करें। इस उद्देश्य के लिए, वे विनिमेय हैं। एनम प्रकार को संदर्भित करते समय आपको थोड़ा अतिरिक्त लिखने की आवश्यकता होगी, लेकिन ऐसा अक्सर नहीं होगा।

struct TextureUploadFormat {
    enum Type : uint32 {
        r,
        rg,
        rgb,
        rgba,
        __count
    };
};

// must use ::Type, which is the extra typing with this method; beats all the static_cast<>()
uint32 getFormatStride(TextureUploadFormat::Type format){
    const uint32 formatStride[TextureUploadFormat::__count] = {
        1,
        2,
        3,
        4
    };
    return formatStride[format]; // decays without complaint
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.