সি ++ তে বিট ফ্ল্যাগের জন্য স্কোপড এনাম ব্যবহার করা


60

একটি enum X : int(সি #) বা enum class X : int(সি ++ 11) এমন এক ধরণের যা একটি লুকানো অভ্যন্তরীণ ক্ষেত্র intযা কোনও মান ধরে রাখতে পারে। এছাড়াও, Xএনামের উপর সংখ্যক পূর্বনির্ধারিত ধ্রুবক সংজ্ঞায়িত করা হয়। এনামটি তার পূর্ণসংখ্যার মান এবং বিপরীতে castালাই সম্ভব। এটি সি # এবং সি ++ 11 উভয় ক্ষেত্রেই সত্য।

সি # তে এনামগুলি কেবলমাত্র পৃথক মান ধরে রাখতে ব্যবহার করা হয় না, মাইক্রোসফ্টের সুপারিশ অনুসারে পতাকাগুলির বিটওয়াইস সংমিশ্রণগুলি ধারণ করতেও ব্যবহৃত হয় । এই জাতীয় এনামগুলি (সাধারণত, তবে অগত্যা নয়) [Flags]গুণাবলী দিয়ে সজ্জিত । বিকাশকারীদের জীবনকে আরও সহজ করার জন্য, বিটওয়াইস অপারেটরগুলি (ওআর, এবং, ইত্যাদি ...) খুব বেশি লোড হয় যাতে আপনি সহজেই (সি #) এর মতো কিছু করতে পারেন:

void M(NumericType flags);

M(NumericType.Sign | NumericType.ZeroPadding);

আমি একজন অভিজ্ঞ সি # বিকাশকারী, তবে কেবল কয়েক দিন ধরে সি ++ প্রোগ্রামিং করছি, এবং আমি সি ++ কনভেনশনগুলির সাথে পরিচিত নই। আমি সি # তে যেমন ব্যবহার করতাম ঠিক ঠিক তেমনভাবে একটি সি ++ 11 এনাম ব্যবহার করার ইচ্ছা করি। সি ++ ১১-এ স্কোপড এনামগুলিতে বিটওয়াইস অপারেটরগুলি অতিরিক্ত লোড হয় না, তাই আমি সেগুলি ওভারলোড করতে চেয়েছিলাম

এটি একটি বিতর্ক চেয়েছিল, এবং তিনটি বিকল্পের মধ্যে মতামতগুলি পৃথক বলে মনে হচ্ছে:

  1. এনাম টাইপের একটি পরিবর্তনশীল সি # এর মতো বিট ফিল্ডটি ধরে রাখতে ব্যবহৃত হয়:

    void M(NumericType flags);
    
    // With operator overloading:
    M(NumericType::Sign | NumericType::ZeroPadding);
    
    // Without operator overloading:
    M(static_cast<NumericType>(static_cast<int>(NumericType::Sign) | static_cast<int>(NumericType::ZeroPadding)));

    তবে এটি সি ++ 11 এর স্কোপড এনামগুলির দৃ strongly়ভাবে টাইপযুক্ত এনাম দর্শনের বিরুদ্ধে লড়াই করতে পারে।

  2. আপনি যদি এনামগুলির বিটওয়াইস সংমিশ্রণটি সঞ্চয় করতে চান তবে একটি সরল পূর্ণসংখ্যা ব্যবহার করুন:

    void M(int flags);
    
    M(static_cast<int>(NumericType::Sign) | static_cast<int>(NumericType::ZeroPadding));

    তবে এটি সমস্ত কিছু হ্রাস করবে int, আপনাকে পদ্ধতিতে কী ধরণের প্রকারটি রাখবেন সে সম্পর্কে কোনও ধারণা ছাড়াই আপনাকে ছেড়ে দেবে।

  3. একটি পৃথক শ্রেণি লিখুন যা অপারেটরদের ওভারলোড করে এবং কোনও লুকানো পূর্ণসংখ্যার ক্ষেত্রে বিটওয়াইস পতাকাগুলি রাখবে:

    class NumericTypeFlags {
        unsigned flags_;
    public:
        NumericTypeFlags () : flags_(0) {}
        NumericTypeFlags (NumericType t) : flags_(static_cast<unsigned>(t)) {}
        //...define BITWISE test/set operations
    };
    
    void M(NumericTypeFlags flags);
    
    M(NumericType::Sign | NumericType::ZeroPadding);

    ( ব্যবহারকারী 315052 দ্বারা সম্পূর্ণ কোড )

    তবে তারপরে আপনার কোনও ইন্টেলিসেন্স বা সম্ভাব্য মানগুলি আপনাকে ইঙ্গিত করার জন্য কোনও সমর্থন নেই।

আমি জানি এটি একটি বিষয়গত প্রশ্ন , কিন্তু: আমার কোন পদ্ধতির ব্যবহার করা উচিত? কোন পদ্ধতির, যদি থাকে তবে সি ++ তে সর্বাধিক পরিচিতি পাওয়া যায়? বিট ক্ষেত্রগুলি নিয়ে কাজ করার সময় আপনি কোন পন্থা ব্যবহার করেন এবং কেন ?

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

উদাহরণস্বরূপ, আমার সি # ব্যাকগ্রাউন্ডের কারণে আমি সি ++ এ 1 পদ্ধতির সাথে যেতে চাই। এটির সাথে আমার বাড়তি পরিবেশের সম্ভাব্য মানগুলি সম্পর্কে ইঙ্গিত করতে পারে এমন অতিরিক্ত সুবিধা রয়েছে এবং ওভারলোডেড এনাম অপারেটরগুলির সাথে এটি লিখতে ও বোঝা সহজ এবং বেশ পরিষ্কার। এবং পদ্ধতি স্বাক্ষরটি পরিষ্কারভাবে দেখায় যে এটি কোন ধরণের মান প্রত্যাশা করে। তবে এখানকার বেশিরভাগ লোকই আমার সাথে একমত নন, সম্ভবত সঙ্গত কারণেই।


2
আইএসও সি ++ কমিটি স্পষ্টভাবে উল্লেখ করার জন্য পর্যাপ্ত 1 বিকল্পটি খুঁজে পেয়েছিল যে এনামগুলির মান পরিসীমাতে পতাকাগুলির সমস্ত বাইনারি সংমিশ্রণ রয়েছে। (এটি সি ++ 03 এর পূর্বাভাস দেয়) তাই এই কিছুটা বিষয়গত প্রশ্নের একটি উদ্দেশ্যমূলক অনুমোদন রয়েছে।
এমসাল্টার

1
(@ এসএমএলটারদের মন্তব্য স্পষ্ট করতে, একটি সি ++ এনামের পরিসর তার অন্তর্নিহিত ধরণের উপর ভিত্তি করে (যদি একটি নির্দিষ্ট প্রকারের হয়)) বা অন্যথায় এটির সংখ্যকগণের উপর নির্ভর করে the পরবর্তী ক্ষেত্রে, পরিসীমাটি সর্বনিম্ন বিটফিল্ডের উপর ভিত্তি করে যা সমস্ত সংজ্ঞায়িত গণককে ধরে রাখতে পারে ; উদাহরণস্বরূপ, enum E { A = 1, B = 2, C = 4, };ব্যাপ্তিটি 0..7(3 বিট) Thus সুতরাং, সি ++ স্ট্যান্ডার্ড স্পষ্টভাবে গ্যারান্টি দেয় যে # 1 সর্বদা কার্যকর বিকল্প হবে [[বিশেষত অন্যথায় সুনির্দিষ্ট না করা হলে enum classডিফল্ট enum class : intএবং এভাবে সর্বদা একটি নির্দিষ্ট অন্তর্নিহিত ধরণের থাকে]]
জাস্টিন সময়

উত্তর:


31

সবচেয়ে সহজ উপায় হ'ল অপারেটরটি নিজেকে ওভারলোড সরবরাহ করে। আমি টাইপ প্রতি বেসিক ওভারলোডগুলি প্রসারিত করতে একটি ম্যাক্রো তৈরি করার কথা ভাবছি।

#include <type_traits>

enum class SBJFrameDrag
{
    None = 0x00,
    Top = 0x01,
    Left = 0x02,
    Bottom = 0x04,
    Right = 0x08,
};

inline SBJFrameDrag operator | (SBJFrameDrag lhs, SBJFrameDrag rhs)
{
    using T = std::underlying_type_t <SBJFrameDrag>;
    return static_cast<SBJFrameDrag>(static_cast<T>(lhs) | static_cast<T>(rhs));
}

inline SBJFrameDrag& operator |= (SBJFrameDrag& lhs, SBJFrameDrag rhs)
{
    lhs = lhs | rhs;
    return lhs;
}

(দ্রষ্টব্য এটি type_traitsএকটি সি ++ 11 শিরোনাম এবং std::underlying_type_tএটি একটি সি ++ 14 বৈশিষ্ট্য)


6
স্ট্যান্ড :: অন্তর্নিহিত_প্রকার_টি হল সি ++ ১৪। সি ++ 11 এ টাইপ করুন স্ট্যান্ড :: আন্ডারলাইং_ টাইপ <টি> :: টাইপ করুন।
ddevienne

14
আপনি কেন ইনপুটটির static_cast<T>জন্য ব্যবহার করছেন তবে ফলাফলের জন্য সি-স্টাইলের castালাই কেন ?
রুস্লান

2
@ রাস্লান প্রথম এই প্রশ্নটি দ্বিতীয়
অডিও ফ্যান্যাটিক

আপনি ইতিমধ্যে যখন ইন্টিগ্রেটেড জানেন তখনও আপনি কেন স্ট্যান্ড :: অন্তর্নিহিত_ টাইপ_টি নিয়ে বিরক্ত করছেন?
poizan42

1
যদি SBJFrameDragকোনও ক্লাসে সংজ্ঞায়িত করা হয় এবং |পরে-অপারেটরটি একই শ্রেণীর সংজ্ঞায় ব্যবহৃত হয়, আপনি অপারেটরটিকে এমন কীভাবে সংজ্ঞায়িত করবেন যে এটি শ্রেণীর মধ্যে ব্যবহার করা যেতে পারে?
হ্যালো গুডবাই

6

Orতিহাসিকভাবে, আমি সর্বদা বিট ধ্রুবকের নামকরণের জন্য পুরাতন (দুর্বল টাইপযুক্ত) গণনাটি ব্যবহার করতাম এবং ফলাফলের পতাকাটি সংরক্ষণের জন্য কেবল স্টোরেজ ক্লাসটি স্পষ্টভাবে ব্যবহার করতাম। এখানে, আমার গণনাগুলি স্টোরেজ ধরণের ক্ষেত্রে উপযুক্ত কিনা তা নিশ্চিত করার জন্য এবং ক্ষেত্র এবং এর সাথে সম্পর্কিত ধ্রুবকের মধ্যে অ্যাসোসিয়েশনের উপর নজর রাখার জন্য আমার উপর এই কাজটি হবে।

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

উদাহরণস্বরূপ, বিটওয়াস ধরে ধরে বা অতিরিক্ত চাপ পড়েছে:

enum class E1 { A=1, B=2, C=4 };
void test(E1 e) {
    switch(e) {
    case E1::A: do_a(); break;
    case E1::B: do_b(); break;
    case E1::C: do_c(); break;
    default:
        illegal_value();
    }
}
// ...
test(E1::A); // ok
test(E1::A | E1::B); // nope

আপনার তৃতীয় বিকল্পের জন্য, গণনার স্টোরেজ প্রকারটি বের করতে আপনার কিছু বয়লারপ্লেট দরকার। ধরে নিই আমরা একটি স্বাক্ষরবিহীন অন্তর্নিহিত ধরণের উপর চাপ দিতে চাই (আমরা আরও স্বল্পতর কোড সহ স্বাক্ষরিত হ্যান্ডেল করতে পারি):

template <size_t Size> struct IntegralTypeLookup;
template <> struct IntegralTypeLookup<sizeof(int64_t)> { typedef uint64_t Type; };
template <> struct IntegralTypeLookup<sizeof(int32_t)> { typedef uint32_t Type; };
template <> struct IntegralTypeLookup<sizeof(int16_t)> { typedef uint16_t Type; };
template <> struct IntegralTypeLookup<sizeof(int8_t)>  { typedef uint8_t Type; };

template <typename IntegralType> struct Integral {
    typedef typename IntegralTypeLookup<sizeof(IntegralType)>::Type Type;
};

template <typename ENUM> class EnumeratedFlags {
    typedef typename Integral<ENUM>::Type RawType;
    RawType raw;
public:
    EnumeratedFlags() : raw() {}
    EnumeratedFlags(EnumeratedFlags const&) = default;

    void set(ENUM e)   { raw |=  static_cast<RawType>(e); }
    void reset(ENUM e) { raw &= ~static_cast<RawType>(e); };
    bool test(ENUM e) const { return raw & static_cast<RawType>(e); }

    RawType raw_value() const { return raw; }
};
enum class E2: uint8_t { A=1, B=2, C=4 };
typedef EnumeratedFlags<E2> E2Flag;

এটি আপনাকে এখনও ইন্টেলিজেন্স বা স্বয়ঃপূরণ দেয় না, তবে সংগ্রহের ধরণ সনাক্তকরণটি আমি প্রাথমিকভাবে প্রত্যাশার চেয়ে কম কুৎসিত।


এখন, আমি একটি বিকল্প খুঁজে পেয়েছি: আপনি দুর্বল-টাইপযুক্ত গণনার জন্য স্টোরেজ প্রকারটি নির্দিষ্ট করতে পারেন। এমনকি এটি সি # এর মতো একই সিনট্যাক্স রয়েছে

enum E4 : int { ... };

যেহেতু এটি দুর্বলভাবে টাইপযুক্ত, এবং অন্তর্নিহিতভাবে ইন / থেকে রূপান্তরিত হয় (বা আপনি যে কোনও স্টোরেজ ধরণের চয়ন করেন), এটি মানগুলিকে কম অদ্ভুত বলে মনে করে যা গণনা করা ধ্রুবকের সাথে মেলে না।

ক্ষয়ক্ষতিটি হ'ল এটিকে "ক্রান্তিকাল" হিসাবে বর্ণনা করা হয়েছে ...

বিশেষ দ্রষ্টব্য। এই রূপটি নেস্টেড এবং ঘের বন্ধের সুযোগ উভয়টিতে তার স্বীকৃত স্থিতি যুক্ত করে, তবে আপনি একটি নেমস্পেস দিয়ে এটিকে ঘিরে কাজ করতে পারেন:

namespace E5 {
    enum Enum : int { A, B, C };
}
E5::Enum x = E5::A; // or E5::Enum::A

1
দুর্বলভাবে টাইপ করা এনামগুলির আরেকটি নেতিবাচক দিকটি হ'ল তাদের ধ্রুবকগুলি আমার নাম স্থানটিকে দূষিত করে, যেহেতু এনাম নামের সাথে তাদের উপসর্গের প্রয়োজন হয় না। এবং যদি একই নামের সদস্য সহ আপনার দুটি ভিন্ন এনাম থাকে তবে এটি সমস্ত ধরণের অদ্ভুত আচরণের কারণও হতে পারে।
ড্যানিয়েল এএ পেলসমেকার

সেটা সত্য. নির্দিষ্ট স্টোরেজ প্রকারের সাথে দুর্বল-টাইপযুক্ত বৈকল্পিকটি এর ধ্রুবকগুলিকে উভয়টি এনকোলেসিং স্কোপ এবং তার নিজস্ব সুযোগ, আইআইইউকে যুক্ত করে।
অকেজো

অপ্রকাশিত গণক কেবল পার্শ্ববর্তী স্কোপেই ঘোষণা করা হয়। এটি এনাম-নাম দিয়ে যোগ্যতা অর্জনের জন্য দেখার নিয়মের একটি অংশ, ঘোষণা নয়। সি ++ ১১ 7.2 / 10: প্রতিটি এনাম-নাম এবং প্রতিটি অনির্দিষ্ট শোধককারী এনভোমরেটরটি এমন স্কোপে ঘোষণা করা হয় যা অবিলম্বে এনাম-স্পেসিফায়ার ধারণ করে contains প্রতিটি স্কোপযুক্ত গণককে গণনার পরিধি হিসাবে ঘোষণা করা হয়। এই নামগুলি (3.3) এবং (3.4) সমস্ত নামের জন্য সংজ্ঞায়িত স্কোপ বিধি মান্য করে।
লার্স ভিক্লুন্ড

1
সি ++ 11 এর সাথে আমরা স্ট্যান্ড :: অন্তর্নিহিত_প্রকার যা এনামের অন্তর্নিহিত ধরণের সরবরাহ করে। সুতরাং আমাদের কাছে 'টেমপ্লেট <টাইপনেম ইন্টিগ্রাল টাইপ> স্ট্রাক্ট ইন্টিগ্রাল {টাইপেডেফ টাইপনেম স্টাড :: অন্তর্নিহিত_প্রকার <IntegralType> :: টাইপ টাইপ; }; C সি ++ ১৪ এ আরও 'সরল <টাইমনেম ইন্টিগ্রাল টাইপ> স্ট্রাক্ট ইন্টিগ্রাল {টাইপএফ স্টাড :: অন্তর্নিহিত_প্রকার_আর <ইনটিজারাল টাইপ> টাইপ করুন; };
এমএসআর

4

আপনি C ++ 11 তে টাইপ-সেফ এনাম ফ্ল্যাগগুলি সংজ্ঞায়িত করতে পারেন std::enable_if। এটি একটি প্রাথমিক বাস্তবায়ন যা কিছু জিনিস অনুপস্থিত হতে পারে:

template<typename Enum, bool IsEnum = std::is_enum<Enum>::value>
class bitflag;

template<typename Enum>
class bitflag<Enum, true>
{
public:
  constexpr const static int number_of_bits = std::numeric_limits<typename std::underlying_type<Enum>::type>::digits;

  constexpr bitflag() = default;
  constexpr bitflag(Enum value) : bits(1 << static_cast<std::size_t>(value)) {}
  constexpr bitflag(const bitflag& other) : bits(other.bits) {}

  constexpr bitflag operator|(Enum value) const { bitflag result = *this; result.bits |= 1 << static_cast<std::size_t>(value); return result; }
  constexpr bitflag operator&(Enum value) const { bitflag result = *this; result.bits &= 1 << static_cast<std::size_t>(value); return result; }
  constexpr bitflag operator^(Enum value) const { bitflag result = *this; result.bits ^= 1 << static_cast<std::size_t>(value); return result; }
  constexpr bitflag operator~() const { bitflag result = *this; result.bits.flip(); return result; }

  constexpr bitflag& operator|=(Enum value) { bits |= 1 << static_cast<std::size_t>(value); return *this; }
  constexpr bitflag& operator&=(Enum value) { bits &= 1 << static_cast<std::size_t>(value); return *this; }
  constexpr bitflag& operator^=(Enum value) { bits ^= 1 << static_cast<std::size_t>(value); return *this; }

  constexpr bool any() const { return bits.any(); }
  constexpr bool all() const { return bits.all(); }
  constexpr bool none() const { return bits.none(); }
  constexpr operator bool() { return any(); }

  constexpr bool test(Enum value) const { return bits.test(1 << static_cast<std::size_t>(value)); }
  constexpr void set(Enum value) { bits.set(1 << static_cast<std::size_t>(value)); }
  constexpr void unset(Enum value) { bits.reset(1 << static_cast<std::size_t>(value)); }

private:
  std::bitset<number_of_bits> bits;
};

template<typename Enum>
constexpr typename std::enable_if<std::is_enum<Enum>::value, bitflag<Enum>>::type operator|(Enum left, Enum right)
{
  return bitflag<Enum>(left) | right;
}
template<typename Enum>
constexpr typename std::enable_if<std::is_enum<Enum>::value, bitflag<Enum>>::type operator&(Enum left, Enum right)
{
  return bitflag<Enum>(left) & right;
}
template<typename Enum>
constexpr typename std::enable_if_t<std::is_enum<Enum>::value, bitflag<Enum>>::type operator^(Enum left, Enum right)
{
  return bitflag<Enum>(left) ^ right;
}

নোট করুন number_of_bitsযে দুর্ভাগ্যক্রমে সংকলকটি পূরণ করতে পারে না, কারণ সি ++ এর একটি অঙ্কের সম্ভাব্য মানগুলি অন্তর্নির্ধারণের কোনও উপায় নেই।

সম্পাদনা: আসলে আমি সংশোধন করে দাঁড়িয়ে আছি, number_of_bitsআপনার জন্য সংকলকটি পূরণ করা সম্ভব ।

নোট করুন এটি একটি অ-অবিচ্ছিন্ন এনাম মান সীমা (বন্যভাবে অকার্যকর) পরিচালনা করতে পারে। আসুন আমরা কেবল এটির মতো এনাম দিয়ে উপরেরটি ব্যবহার করা ভাল ধারণা নয় বা পাগলামি তৈরি হবে:

enum class wild_range { start = 0, end = 999999999 };

তবে এটিকে বিবেচনা করা সমস্ত জিনিসই শেষ পর্যন্ত একটি কার্যকর ব্যবহারযোগ্য সমাধান। কোনও ব্যবহারকারীর সাইড বিটফিডলিংয়ের দরকার নেই, এটি টাইপ-সেফ এবং এর সীমার মধ্যে যতটা দক্ষ তা লাভজনক (আমি std::bitsetএখানে বাস্তবায়ন মানের উপর দৃ on়ভাবে ঝুঁকছি ;))।


আমি নিশ্চিত যে আমি অপারেটরগুলির কিছু ওভারলোডগুলি মিস করেছি।
রুবেনভ

2

আমি ঘৃণা পরের লোকের মতো আমার সি ++ 14 তে ম্যাক্রোগুলিকে ঘৃণা করুন, তবে আমি এটি পুরো জায়গাটিতে ব্যবহার করেছি এবং বেশ উদারভাবেও:

#define ENUM_FLAG_OPERATOR(T,X) inline T operator X (T lhs, T rhs) { return (T) (static_cast<std::underlying_type_t <T>>(lhs) X static_cast<std::underlying_type_t <T>>(rhs)); } 
#define ENUM_FLAGS(T) \
enum class T; \
inline T operator ~ (T t) { return (T) (~static_cast<std::underlying_type_t <T>>(t)); } \
ENUM_FLAG_OPERATOR(T,|) \
ENUM_FLAG_OPERATOR(T,^) \
ENUM_FLAG_OPERATOR(T,&) \
enum class T

হিসাবে সহজ হিসাবে ব্যবহার করা

ENUM_FLAGS(Fish)
{
    OneFish,
    TwoFish,
    RedFish,
    BlueFish
};

এবং, যেমন তারা বলে, প্রমাণটি পুডিংয়ে রয়েছে:

ENUM_FLAGS(Hands)
{
    NoHands = 0,
    OneHand = 1 << 0,
    TwoHands = 1 << 1,
    LeftHand = 1 << 2,
    RightHand = 1 << 3
};

Hands hands = Hands::OneHand | Hands::TwoHands;
if ( ( (hands & ~Hands::OneHand) ^ (Hands::TwoHands) ) == Hands::NoHands)
{
    std::cout << "Look ma, no hands!" << std::endl;
}

আপনি যেমন উপযুক্ত দেখেন তেমন কোনও পৃথক অপারেটরকে নির্বিচারে নির্দ্বিধায় মনে করেন তবে আমার উচ্চ-পক্ষপাতদুষ্ট অভিমত, সি / সি ++ নিম্ন স্তরের ধারণাগুলি এবং স্ট্রিমগুলির সাথে হস্তক্ষেপ করার জন্য এবং আপনি আমার ঠাণ্ডা, মৃত হাতের বাইরে এই বিটওয়াইজ অপারেটরদের ক্রিয় করতে পারেন can এবং আমি আপনার কাছে সমস্ত অপরিষ্কার ম্যাক্রোগুলি এবং বিট-ফ্লিপিং স্পেলের সাথে লড়াই করব যা আমি তাদের ধরে রাখতে পারি।


2
আপনি যদি ম্যাক্রোগুলিকে এত বেশি ঘৃণা করেন তবে ম্যাক্রোগুলির পরিবর্তে কিছু সঠিক টেম্পলেট অপারেটর ব্যবহার করে কেন লিখবেন না? তর্কসাপেক্ষ, টেমপ্লেট পদ্ধতির কারণ আপনি ব্যবহার করতে পারেন উত্তম std::enable_ifসঙ্গে std::is_enumশুধুমাত্র গণিত ধরনের সঙ্গে কাজ করার জন্য আপনার বিনামূল্যে অপারেটর overloads সীমিত করতে। std::underlying_typeশক্তিশালী টাইপিংটি না হারিয়ে ফাঁকটি আরও কমিয়ে আনতে আমি তুলনা অপারেটরগুলি (ব্যবহার করে ) এবং লজিকাল নয় অপারেটর যুক্ত করেছি । কেবলমাত্র আমি যা মেলে না তা হ'ল বুলের অন্তর্নিহিত রূপান্তর, flags != 0এবং !flagsএটি আমার পক্ষে যথেষ্ট।
monkey0506

1

সাধারণত আপনি পূর্ণসংখ্যার মানগুলির একটি সেট নির্ধারণ করতে চান যা একক-বিট সেট বাইনারি সংখ্যার সাথে মিলে যায়, তারপরে সেগুলি একসাথে যুক্ত করুন। সি প্রোগ্রামাররা সাধারণত এটি করে।

সুতরাং আপনারা (মানগুলি সেট করার জন্য বিটশিফ্ট অপারেটর ব্যবহার করে যেমন 1 << 2 বাইনারি 100 এর সমান)

#define ENUM_1 1
#define ENUM_2 1 << 1
#define ENUM_3 1 << 2

ইত্যাদি

সি ++ এ আপনার আরও বিকল্প রয়েছে, একটি নতুন প্রকারের পরিবর্তে এটি একটি ইন্ট ( টাইপডেফ ব্যবহার করুন ) এবং একইভাবে উপরের মত মান নির্ধারণ করুন; বা বিটফিল্ড বা বুলের একটি ভেক্টর সংজ্ঞায়িত করুন । শেষ 2 টি খুব স্পেস দক্ষ এবং পতাকাগুলি নিয়ে কাজ করার জন্য আরও অনেক কিছু বোঝায়। একটি বিটফিল্ড আপনাকে টাইপ চেকিং দেওয়ার সুবিধা দেয় (এবং তাই ইনটেলিসেন্স)।

আমি বলব (স্পষ্টত বিষয়গত) যে কোনও সি ++ প্রোগ্রামার আপনার সমস্যার জন্য একটি বিটফিল্ড ব্যবহার করা উচিত, তবে আমি সি ++ প্রোগ্রামগুলিতে সি প্রোগ্রামগুলি দ্বারা ব্যবহৃত # নির্দিষ্ট সংজ্ঞাটি দেখতে চাই।

আমি মনে করি বিটফিল্ড সি # এর এনামের নিকটতম, কেন সি # বিটফিল্ড টাইপ হওয়ার জন্য একটি এনামকে অতিরিক্ত চাপ দেওয়ার চেষ্টা করেছে - একটি এনাম সত্যই "একক নির্বাচন" টাইপ হওয়া উচিত।


11
সি ++ তে এভাবে ম্যাক্রোগুলি ব্যবহার করা খারাপ
B is

3
সি ++ 14 আপনাকে বাইনারি আক্ষরিক (উদাহরণস্বরূপ 0b0100) সংজ্ঞায়িত করার অনুমতি দেয় যাতে 1 << nফর্ম্যাটটি হ'ল অপ্রচলিত of
রব কে

সম্ভবত আপনি বিটফিল্ডের পরিবর্তে বিটসেট বুঝিয়েছেন।
জর্জে বেলন

1

নীচে এনাম-ফ্ল্যাগগুলির একটি সংক্ষিপ্ত উদাহরণ, দেখতে অনেকটা সি # এর মতো দেখাচ্ছে।

পদ্ধতির বিষয়ে, আমার মতে: কম কোড, কম বাগ, আরও ভাল কোড।

#indlude "enum_flags.h"

ENUM_FLAGS(foo_t)
enum class foo_t
    {
     none           = 0x00
    ,a              = 0x01
    ,b              = 0x02
    };

ENUM_FLAGS(foo2_t)
enum class foo2_t
    {
     none           = 0x00
    ,d              = 0x01
    ,e              = 0x02
    };  

int _tmain(int argc, _TCHAR* argv[])
    {
    if(flags(foo_t::a & foo_t::b)) {};
    // if(flags(foo2_t::d & foo_t::b)) {};  // Type safety test - won't compile if uncomment
    };

ENUM_FLAGS (টি) হ'ল একটি ম্যাক্রো, enum_flags.h এ সংজ্ঞায়িত হয়েছে (এর পরে কম 100 লাইন, কোনও সীমাবদ্ধতা ছাড়াই বিনামূল্যে ব্যবহার করা যাবে)।


1
ফাইল enum_flags.h আপনার প্রশ্নের 1 ম সংশোধন হিসাবে একই? যদি হ্যাঁ, আপনি এটি উল্লেখ করতে সংশোধন URL টি ব্যবহার করতে পারেন: http://programmers.stackexchange.com/revisions/205567/1
gnat

+1 দেখতে সুন্দর, পরিষ্কার দেখাচ্ছে। আমি আমাদের এসডিকে প্রকল্পে এটি চেষ্টা করব।
গ্রেট ক্লোবোন

1
@ গ্রেটক্লেওরন এটিই আমি পরিষ্কার বলি: পেস্ট.বুন্টু.com
শেহ

1
অবশ্যই, ::typeসেখানে মিস । ফিক্সড: paste.ubuntu.com/23884820
sehe

@ ওহে, টেমপ্লেট কোডটি সুগঠিত এবং বোধগম্য হওয়ার কথা নয়। এই জাদু কি? দুর্দান্ত ....
লোলটি

0

বিড়ালের চামড়ার আরও একটি উপায় রয়েছে:

বিট অপারেটরগুলি ওভারলোড করার পরিবর্তে কমপক্ষে কিছু লোক আপনাকে স্কোপড এনামগুলিকে ঘৃণ্য সীমাবদ্ধতা থেকে বাঁচাতে সহায়তা করার জন্য কেবল একটি 4 লাইনার যুক্ত করতে পছন্দ করতে পারে:

#include <cstdio>
#include <cstdint>
#include <type_traits>

enum class Foo : uint16_t { A = 0, B = 1, C = 2 };

// ut_cast() casts the enum to its underlying type.
template <typename T>
inline auto ut_cast(T x) -> std::enable_if_t<std::is_enum_v<T>,std::underlying_type_t<T>>
{
    return static_cast<std::underlying_type_t<T> >(x);
}

int main(int argc, const char*argv[])
{
   Foo foo{static_cast<Foo>(ut_cast(Foo::B) | ut_cast(Foo::C))};
   Foo x{ Foo::C };
   if(0 != (ut_cast(x) & ut_cast(foo)) )
       puts("works!");
    else 
        puts("DID NOT WORK - ARGHH");
   return 0;
}

মঞ্জুরিপ্রাপ্ত, আপনাকে ut_cast()প্রতিবার জিনিসটি টাইপ করতে হবে, তবে উপরের দিকে, এই static_cast<>()অন্তর্নিহিত ধরণের রূপান্তর বা operator uint16_t()ধরণের জিনিসগুলির সাথে তুলনা করে একইভাবে আরও পাঠযোগ্য কোড আসে ।

এবং আসুন আমরা এখানে সত্য কথা বলতে চাই, Fooউপরের কোডটির মতো টাইপ ব্যবহার করে এর ঝুঁকি রয়েছে:

অন্য কোথাও কেউ ভেরিয়েবলের উপরে সুইচ কেস fooকরতে পারে এবং এটির একাধিক মান ধরে রাখার আশা করে না ...

সুতরাং কোডটি লিটারে লিটারিং ut_cast()পাঠকদের সতর্ক করতে সাহায্য করে যে মশালার কিছু চলছে।

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