c++ - क्या डिफ़ॉल्ट विफल मामले के साथ एक सशर्त प्रकार को लागू करने का एक अच्छा तरीका है?




c++11 templates (2)

आप इसे अप्रत्यक्ष स्तर जोड़कर हल कर सकते हैं, ताकि बाहरी कंडीशनल_टी का परिणाम एक प्रकार न हो लेकिन एक मेटाफ़ंक्शन जो इसे लागू करने के लिए ::type आवश्यकता है। इसके बाद enable_if बजाय enable_if उपयोग करें ताकि आप उस तक पहुंच न enable_if_t ::type जब तक कि वास्तव में इसकी आवश्यकता न हो:

template<typename T> struct identity { using type = T; };

template<std::size_t N>
using bit_type = typename
    std::conditional_t<N == std::size_t{  8 }, identity<std::uint8_t>,
    std::conditional_t<N == std::size_t{ 16 }, identity<std::uint16_t>,
    std::conditional_t<N == std::size_t{ 32 }, identity<std::uint32_t>, 
    std::enable_if<N == std::size_t{ 64 }, std::uint64_t>>>>::type;

इस संस्करण में अंतिम शाखा का प्रकार enable_if< condition , uint64_t> जो हमेशा एक मान्य प्रकार होता है, और आपको केवल एक त्रुटि मिलती है यदि वह शाखा वास्तव में ली गई है और enable_if<false, uint64_t>::type की आवश्यकता है। जब पहले वाली शाखाओं में से एक ली जाती है तो आप identity<uintNN_t>::type का उपयोग करके समाप्त हो जाते identity<uintNN_t>::type छोटे पूर्णांक प्रकारों में से एक के लिए identity<uintNN_t>::type , और इससे कोई फर्क नहीं पड़ता है कि enable_if<false, uint64_t> का कोई नेस्टेड प्रकार नहीं है (क्योंकि यह नहीं इसका इस्तेमाल करें)।

एक सशर्त प्रकार लागू करने के लिए मैं std::conditional_t आनंद लेता हूं क्योंकि यह कोड को छोटा और बहुत पठनीय रखता है:

template<std::size_t N>
using bit_type =
    std::conditional_t<N == std::size_t{  8 }, std::uint8_t,
    std::conditional_t<N == std::size_t{ 16 }, std::uint16_t,
    std::conditional_t<N == std::size_t{ 32 }, std::uint32_t, 
    std::conditional_t<N == std::size_t{ 64 }, std::uint64_t, void>>>>;

इसका उपयोग करना काफी सहजता से काम करता है:

bit_type<8u> a;  // == std::uint8_t
bit_type<16u> b; // == std::uint16_t
bit_type<32u> c; // == std::uint32_t
bit_type<64u> d; // == std::uint64_t

लेकिन चूंकि यह एक शुद्ध सशर्त प्रकार है इसलिए इस मामले में एक डिफ़ॉल्ट प्रकार - void होना चाहिए। इसलिए यदि N कोई अन्य मान है तो पैदावार टाइप करें:

bit_type<500u> f; // == void

अब यह संकलन नहीं है, लेकिन उपज का प्रकार अभी भी मान्य है।

मतलब आप bit_type<500u>* f; कह सकते हैं bit_type<500u>* f; और एक वैध कार्यक्रम होगा!

तो क्या सशर्त प्रकार के असफल मामले तक पहुंचने पर संकलन को विफल होने का एक अच्छा तरीका है?

एक विचार तुरंत अंतिम std::conditional_t को बदलना होगा: std::enable_if_t को std::enable_if_t साथ std::enable_if_t :

template<std::size_t N>
using bit_type =
    std::conditional_t<N == std::size_t{  8 }, std::uint8_t,
    std::conditional_t<N == std::size_t{ 16 }, std::uint16_t,
    std::conditional_t<N == std::size_t{ 32 }, std::uint32_t, 
    std::enable_if_t<  N == std::size_t{ 64 }, std::uint64_t>>>>;

इसके साथ समस्या यह है कि टेम्पलेट हमेशा पूरी तरह से मूल्यांकन किए जाते हैं, जिसका अर्थ है कि std::enable_if_t हमेशा पूरी तरह से मूल्यांकन किया जाता है - और यह विफल हो जाएगा यदि N != std::size_t{ 64 } । Urgh।

इसके लिए मेरा वर्तमान में जाना उल्टा है, बल्कि अनाड़ी है जो घोषणाओं using एक संरचना और 3 पेश using :

template<std::size_t N>
struct bit_type {
private:
    using vtype =
        std::conditional_t<N == std::size_t{ 8 }, std::uint8_t,
        std::conditional_t<N == std::size_t{ 16 }, std::uint16_t,
        std::conditional_t<N == std::size_t{ 32 }, std::uint32_t,
        std::conditional_t<N == std::size_t{ 64 }, std::uint64_t, void>>>>;

public:
    using type = std::enable_if_t<!std::is_same_v<vtype, void>, vtype>;
};

template<std::size_t N>
using bit_type_t = bit_type<N>::type;

static_assert(std::is_same_v<bit_type_t<64u>, std::uint64_t>, "");

जो आम तौर पर काम करता है, लेकिन मैं इसे नापसंद करता हूं क्योंकि यह इतना सामान जोड़ता है, मैं शायद सिर्फ टेम्पलेट विशेषज्ञता का उपयोग कर सकता हूं। यह एक विशेष प्रकार के रूप में void को भी सुरक्षित रखता है - इसलिए यह काम नहीं करेगा जहां void वास्तव में एक शाखा से उपज है। क्या कोई पठनीय, लघु समाधान है?


बस मज़े के लिए ... std::tuple का उपयोग करने के बारे में क्या std::tuple और std::tuple_element से बचने के लिए सभी std::conditional ?

यदि आप C ++ 14 (तो टेम्प्लेट वेरिएबल्स और टेम्प्लेट वेरिएबल्स की विशेषज्ञता) का उपयोग कर सकते हैं, तो आप रूपांतरण आकार / इंडेक्स-इन-ट्यूपल के लिए एक टेम्प्लेट चर लिख सकते हैं।

template <std::size_t>
constexpr std::size_t  bt_index = 100u; // bad value

template <> constexpr std::size_t  bt_index<8u>  = 0u; 
template <> constexpr std::size_t  bt_index<16u> = 1u; 
template <> constexpr std::size_t  bt_index<32u> = 2u; 
template <> constexpr std::size_t  bt_index<64u> = 3u; 

इसलिए bit_type बन गया

template <std::size_t N>
using bit_type = std::tuple_element_t<bt_index<N>,
   std::tuple<std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t>>;

यदि आप केवल C ++ 11 का उपयोग कर सकते हैं, तो आप एक bt_index() constexpr फ़ंक्शन विकसित कर सकते हैं जो सही (या गलत) मान लौटाता है।

आप सत्यापित कर सकते हैं कि संतुष्ट हैं

static_assert( std::is_same_v<bit_type<8u>,  std::uint8_t>, "!" );
static_assert( std::is_same_v<bit_type<16u>, std::uint16_t>, "!" );
static_assert( std::is_same_v<bit_type<32u>, std::uint32_t>, "!" );
static_assert( std::is_same_v<bit_type<64u>, std::uint64_t>, "!" );

और जो असमर्थित आयाम के साथ bit_type का उपयोग कर रहा है

bit_type<42u> * pbt42;

एक संकलन त्रुटि का कारण।

EDIT - जैसा कि जोनाथन वेकली द्वारा सुझाया गया है, अगर आप C ++ 20 का उपयोग कर सकते हैं, तो std::ispow2() और std::log2p1() , आप बहुत सरल कर सकते हैं: आप bt_index से बच सकते हैं और बस लिख सकते हैं

template <std::size_t N>
using bit_type = std::tuple_element_t<std::ispow2(N) ? std::log2p1(N)-4u : -1,
   std::tuple<std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t>>;




conditional-types