দৃ automatically়ভাবে টাইপ করা এনুমকে কীভাবে স্বয়ংক্রিয়ভাবে রূপান্তর করবেন?


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স্বাভাবিক enums, পূর্ণসংখ্যা টাইপ রূপান্তরিত করা যাবে যখন শক্তিশালী ভাবে টাইপ enums একটি ঢালাই ছাড়াই এটি ব্যবহার করতে পারবেন না কি শক্তিশালী ভাবে টাইপ enum অর্জন করার চেষ্টা করা হয়, কিন্তু সেখানে একটি ছোট পার্থক্য হয়।

সুতরাং, দৃ strongly়ভাবে টাইপ করা এনাম মানটিকে কোনও castালাই ছাড়াই একটি পূর্ণসংখ্যার ধরণের রূপান্তর করার কোনও উপায় আছে কি? যদি হ্যাঁ, কিভাবে?

উত্তর:


134

একাধিক সমস্যা সমাধানের লক্ষ্যে দৃ typ়ভাবে টাইপ করা এনামগুলি এবং আপনার প্রশ্নে যেমন উল্লেখ করেছেন কেবল স্কোপিং সমস্যা নয়:

  1. প্রকারের সুরক্ষা সরবরাহ করুন, সুতরাং এটি অবিচ্ছেদ্য প্রচারের মাধ্যমে পূর্ণসংখ্যায় অন্তর্নিহিত রূপান্তরকে সরিয়ে দেয়।
  2. অন্তর্নিহিত প্রকারগুলি নির্দিষ্ট করুন।
  3. শক্তিশালী স্কোপিং সরবরাহ করুন।

সুতরাং, দৃ strongly়ভাবে টাইপ করা এনামকে পূর্ণসংখ্যায় রূপান্তর করা অসম্ভব, এমনকি এর অন্তর্নিহিত ধরণের - এটিই ধারণা। সুতরাং আপনাকে static_castরূপান্তরটি সুস্পষ্ট করতে ব্যবহার করতে হবে।

যদি আপনার একমাত্র সমস্যাটি স্কোপিং হয় এবং আপনি সত্যিকার অর্থে পূর্ণসংখ্যার সাথে অন্তর্নিহিত প্রচার পেতে চান তবে আপনি যে কাঠামোটিতে ঘোষণা করা হয়েছে তার সুযোগের সাথে দৃ with়ভাবে টাইপ করা এনুম ব্যবহার না করা ভাল।


2
এটি C ++ স্রষ্টাদের কাছ থেকে 'আপনি কী করতে চান তা আমরা আরও ভালভাবে জানি' এর আরও একটি অদ্ভুত উদাহরণ। প্রচলিত (পুরানো-শৈলী) এনামগুলির প্রচুর সুবিধাগুলি ছিল সূচকগুলিতে অন্তর্নিহিত রূপান্তর, বিটওয়াইজ ক্রিয়াকলাপগুলি নির্বিঘ্নে ব্যবহার করা ইত্যাদি The অন্তর্নিহিত ধরণের স্পেসিফিকেশন!)। সুতরাং আপনি এখন স্ট্র্যাটে :: ভেক্টরকে ঘিরে আপনার নিজের মোড়ক তৈরির মতো নতুন কৌশলগুলির মতো কৌশলগুলি সহ পুরানো স্টাইলের এনগগুলি ব্যবহার করতে বাধ্য হন বা স্ট্রোক :: ভেক্টরকে ঘিরে আপনার নিজের মোড়ক তৈরি করার মতো কুশলী কাজ তৈরি করতে বাধ্য হন। কোন মন্তব্য নেই
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

আর। মার্টিনহো ফার্নান্দেসের দেওয়া উত্তরের সি ++ 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
এটি টাইপিং বা কোড ক্লিনার তৈরি হ্রাস করে না এবং বড় প্রকল্পগুলিতে এই ধরনের অন্তর্নিহিত রূপান্তরগুলি খুঁজে পাওয়া আরও শক্ত করে তোলার পার্শ্ব প্রতিক্রিয়া রয়েছে has স্ট্যাটিক_কাস্ট এই কনস্ট্রাক্টগুলির চেয়ে প্রজেক্ট অনুসন্ধান করা আরও সহজ হবে।
অতুল কুমার

3
@ অতুলকুমার কীভাবে স্থির_কাস্টের অনুসন্ধান টু_েনাম অনুসন্ধানের চেয়ে সহজ?
জোহান জেরেল

1
এই উত্তরের কিছু ব্যাখ্যা এবং ডকুমেন্টেশন দরকার।
অরবিটে

17

নং নেই কোন প্রাকৃতিক উপায়

আসলে, enum classসি ++ 11 তে দৃ strongly়ভাবে টাইপ করার পেছনের অন্যতম উদ্দেশ্য হল তাদের নীরব রূপান্তরটি প্রতিরোধ করা int


খুরশিদ নর্মারাদোভের কাছ থেকে জবাবটি দেখুন। এটি 'প্রাকৃতিক উপায়ে' আসে এবং এটি 'দ্য সি ++ প্রোগ্রামিং ল্যাঙ্গুয়েজ (চতুর্থ সংস্করণ)' তে লক্ষ্য করা যায়। এটি কোনও 'স্বয়ংক্রিয় উপায়ে' আসে না এবং এটি এটি সম্পর্কে ভাল।
PapaAtHome

@ পপাআটহোম স্থির_কাস্টের ওপরের সুবিধাটি আমি বুঝতে পারি না। টাইপিং বা কোড পরিষ্কার করার ক্ষেত্রে খুব বেশি পরিবর্তন হয় না। এখানে প্রাকৃতিক উপায় কি? একটি ফাংশন ফেরত মান?
অতুল কুমার

1
@ ইউজার ২7769 62 62২ সুবিধাটি আমার পক্ষে এটি হ'ল এটি স্বয়ংক্রিয় বা 'নীরব' নয় যেমন ইহমিলিন্ড রাখে। এটি ত্রুটিগুলি খুঁজে পেতে স্বতন্ত্র প্রতিরোধ করে। আপনি এখনও একটি কাস্ট করতে পারেন তবে আপনাকে এটি সম্পর্কে ভাবতে বাধ্য করা হবে। আপনি কী করছেন তা আপনি জানেন। আমার কাছে এটি 'নিরাপদ কোডিং' অভ্যাসের একটি অংশ। আমি পছন্দ করি যে কোনও রূপান্তর স্বয়ংক্রিয়ভাবে সম্পন্ন হয় না এমন কোনও সুযোগের স্লাইভ রয়েছে যা এটি একটি ত্রুটি প্রবর্তন করতে পারে। টাইপ সিস্টেমের সাথে সম্পর্কিত সি ++ 11 এ বেশ কয়েকটি পরিবর্তন আপনি যদি আমাকে জিজ্ঞাসা করেন তবে এই বিভাগে পড়ে।
পাপাআটহোম

17

অন্তর্ভুক্ত রূপান্তর (ডিজাইনের মাধ্যমে) অনুপস্থিতির কারণ অন্যান্য উত্তরে দেওয়া হয়েছিল।

আমি ব্যক্তিগতভাবে operator+এনাম ক্লাস থেকে তাদের অন্তর্নিহিত ধরণের রূপান্তরের জন্য অ্যানারি ব্যবহার করি :

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;

যেখানে আমি প্রকৃতপক্ষে এক শটটিতে এনাম এবং অপারেটর তৈরি করতে ম্যাক্রো ব্যবহার করি।

#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
একে "টাইপ পেনিং" বলা হয় এবং যদিও কিছু সংকলক দ্বারা সমর্থিত এটি পোর্টেবল নয়, কারণ সি ++ স্ট্যান্ডার্ড বলছে যে আপনি সেট করার পরে un.iএটি "সক্রিয় সদস্য" এবং আপনি কেবল একটি ইউনিয়নের সক্রিয় সদস্য পড়তে পারবেন।
জোনাথন ওয়াকলি

6
@ জোনাথন ওয়েইকলি আপনি প্রযুক্তিগতভাবে সঠিক, তবে আমি কোনও সংকলক কখনও দেখিনি যেখানে এটি নির্ভরযোগ্যভাবে কাজ করে না। এর মতো স্টাফ, বেনামে ইউনিয়ন এবং # প্রগমা একবার ডিফাক্টো স্ট্যান্ডার্ড।
বিগস্যান্ডউইচ

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
এটি যে কোনও প্রকারের সুরক্ষা যোগ করে না (প্রকৃতপক্ষে, আপনি কেবল প্রকারের সুরক্ষাটি সরিয়ে দিয়েছেন) - এটি কেবল নাম স্কোপিং যুক্ত করে।
অরবিটে

নিবন্ধন করুন হ্যালো আমি সম্মত। আমি মিথ্যে বলেছি. প্রযুক্তিগতভাবে, নির্ভুলভাবে বলতে গেলে, প্রকারটি কেবল একটি নাম / স্থানের নীচে এবং সম্পূর্ণরূপে যোগ্যতা অর্জন করে 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সিনট্যাক্স দ্বারা সংজ্ঞায়িত যে কোনও সম্ভাব্য বস্তুকে সাধারণকরণ এবং উপহাস করতে ।


কিন্তু খ 1 এবং B2 বর্গ বাহিরে সংজ্ঞায়িত হবে ... অথবা এই ক্ষেত্রে জন্য অব্যবহারযোগ্য হয় - header.h <- বর্গ বি - main.cpp <---- myvector.push_back (খ 1)
Fl0

? না একটি হতে "স্ট্যাটিক constexpr B" পরিবর্তে "স্ট্যাটিক constexpr int- এ 'তা না হলে, খ :: খ 1 মাত্র সব সময়ে কোন typesafety সঙ্গে কোন int হয়।
কিছু লোক

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

সি ++ কমিটি একধাপ এগিয়ে (গ্লোবাল নেমস্পেসের বাইরে এনামগুলিকে স্কোপ করে) এবং পঞ্চাশ ধাপ পিছনে নিয়েছিল (কোনও পূর্ণসংখ্যার এনাম টাইপ ক্ষয় নয়)। দুঃখের বিষয়, enum classকোনও এন-সিম্বলিক উপায়ে আপনার এনামের মান প্রয়োজন হলে তা কেবল ব্যবহারযোগ্য নয়।

সর্বোত্তম সমাধান হ'ল এটি একেবারেই ব্যবহার না করা এবং এর পরিবর্তে একটি এনপেস বা স্ট্রাক্ট ব্যবহার করে এনামকে সুযোগ দিন। এই উদ্দেশ্যে, তারা বিনিময়যোগ্য। এনাম টাইপ নিজেই উল্লেখ করার সময় আপনাকে কিছুটা অতিরিক্ত টাইপ করতে হবে তবে সম্ভবত এটি প্রায়শই হবে না।

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
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.