[[no_unique_address]] और एक ही प्रकार के दो सदस्य मान


16

[[no_unique_address]]में साथ खेल रहा हूँ c++20

Cppreference पर उदाहरण में हमारे पास एक खाली प्रकार Emptyऔर प्रकार हैZ

struct Empty {}; // empty class

struct Z {
    char c;
    [[no_unique_address]] Empty e1, e2;
};

जाहिर है, के आकार Zकम से कम होना चाहिए 2क्योंकि के प्रकार e1और e2समान हैं।

हालांकि, मैं वास्तव में Zआकार के साथ रहना चाहता हूं 1। यह मुझे सोच रहा था, Emptyकुछ रैपर क्लास में अतिरिक्त टेम्पलेट पैरामीटर के साथ लपेटने के बारे में क्या है जो विभिन्न प्रकार के e1और लागू करता है e2

template <typename T, int i>
struct Wrapper : public T{};

struct Z1 {
    char c;
    [[no_unique_address]] Wrapper<Empty,1> e1;
    [[no_unique_address]] Wrapper<Empty,2> e2;
};

दुर्भाग्य से, sizeof(Z1)==2। क्या एक होने के लिए आकार बनाने की कोई चाल है Z1?

मैं इसके साथ परीक्षण कर रहा हूं gcc version 9.2.1औरclang version 9.0.0


मेरे आवेदन में, मेरे पास बहुत सारे खाली प्रकार हैं

template <typename T, typename S>
struct Empty{
    [[no_unique_address]] T t;
    [[no_unique_address]] S s;
};

जो एक खाली प्रकार है अगर Tऔर Sभी खाली प्रकार और अलग हैं! मैं चाहता हूं कि यह प्रकार भले ही खाली हो Tऔर Sसमान प्रकार के हों।


2
Tखुद के लिए टेम्पलेट तर्क जोड़ने के बारे में क्या ? यह विशिष्ट प्रकार उत्पन्न करेगा। अभी तथ्य यह है कि दोनों Wrapperसे विरासत में Tआप को वापस पकड़ रहा है ...
मैक्स लैंगहॉफ

@MaxLanghof क्या आप के लिए एक टेम्पलेट तर्क जोड़कर मतलब है T? अभी, Tएक टेम्पलेट तर्क है।
टॉम

से विरासत में नहीं मिला T
Evg

@ ईवीजी से यहां कोई फर्क नहीं पड़ता।
एरोरिका

2
: सिर्फ इसलिए कि यह 1 से बड़ा है यह गैर खाली नहीं है coliru.stacked-crooked.com/a/51aa2be4aff4842e
Deduplicator

जवाबों:


6

जो एक खाली प्रकार है अगर Tऔर Sभी खाली प्रकार और अलग हैं! मैं चाहता हूं कि यह प्रकार भले ही खाली हो Tऔर Sसमान प्रकार के हों।

आप ऐसा नहीं कर सकते। तकनीकी रूप से, आप यह भी गारंटी नहीं दे सकते हैं कि यह खाली होगा भले ही Tऔर Sविभिन्न प्रकार के खाली हों। याद रखें: no_unique_addressएक विशेषता है; वस्तुओं को छिपाने के लिए इसकी क्षमता पूरी तरह से कार्यान्वयन-निर्भर है। मानकों के परिप्रेक्ष्य से, आप खाली वस्तुओं के आकार को लागू नहीं कर सकते।

C ++ 20 कार्यान्वयन परिपक्व होने के नाते, आपको यह मान लेना चाहिए कि [[no_unique_address]]आम तौर पर खाली आधार अनुकूलन के नियमों का पालन किया जाएगा। अर्थात्, जब तक एक ही प्रकार की दो वस्तुएँ सबोबिज नहीं होती हैं, आप शायद छिपने की उम्मीद कर सकते हैं। लेकिन इस बिंदु पर, यह पॉट-लक की तरह है।

के विशिष्ट मामले के लिए Tऔर Sएक ही प्रकार के होने के नाते, यह बस संभव नहीं है। "No_unique_address" नाम के निहितार्थों के बावजूद, वास्तविकता यह है कि C ++ के लिए आवश्यक है कि, एक ही प्रकार की वस्तुओं को दो पॉइंटर्स दिए जाएं, वे पॉइंटर्स या तो एक ही ऑब्जेक्ट को इंगित करते हैं या अलग-अलग पते होते हैं। मैं इसे "विशिष्ट पहचान नियम" कहता हूं, और no_unique_addressयह प्रभावित नहीं करता है। से [intro.object] / 9 :

ओवरलैपिंग जीवनकाल वाली दो वस्तुएं जो बिट-फ़ील्ड नहीं हैं, एक ही पता हो सकता है यदि एक दूसरे के भीतर नेस्टेड है, या यदि कम से कम एक शून्य आकार का सबोबेज है और वे विभिन्न प्रकार के हैं ; अन्यथा, उनके पास अलग-अलग पते हैं और भंडारण के असमान बाइट्स पर कब्जा है।

[[no_unique_address]]शून्य प्रकार के घोषित सदस्य शून्य आकार के होते हैं, लेकिन एक ही प्रकार के होने से यह असंभव हो जाता है।

दरअसल, इसके बारे में सोचना, घोंसले के माध्यम से खाली प्रकार को छिपाने का प्रयास अभी भी अद्वितीय पहचान नियम का उल्लंघन करता है। अपने विचार करें Wrapperऔर Z1मामला। यह देखते हुए z1कि इसका एक उदाहरण है Z1, यह स्पष्ट है कि z1.e1और z1.e2विभिन्न प्रकारों के साथ अलग-अलग वस्तुएं हैं। हालांकि, न तो इसके z1.e1भीतर निहित है z1.e2और न ही इसके विपरीत। और जबकि उनके पास विभिन्न प्रकार हैं, (Empty&)z1.e1और विभिन्न प्रकार नहीं(Empty&)z1.e2 हैं । लेकिन वे विभिन्न वस्तुओं की ओर इशारा करते हैं।

और विशिष्ट पहचान नियम के अनुसार, उनके पास अलग-अलग पते होने चाहिए । इसलिए, भले ही e1और e2नाममात्र अलग-अलग प्रकार के हैं, उनके इंटर्नल को भी उसी ऑब्जेक्ट में अन्य सबोबिज के खिलाफ अद्वितीय पहचान का पालन करना होगा। रिकर्सिवली।

आप जो चाहते हैं वह सी ++ में बस असंभव है क्योंकि यह वर्तमान में खड़ा है, भले ही आप कैसे भी प्रयास करें।


महान व्याख्या, बहुत बहुत धन्यवाद!
टॉम

2

जहां तक ​​मैं बता सकता हूं, यदि आप दोनों सदस्य चाहते हैं तो यह संभव नहीं है। लेकिन आप विशेषज्ञ और प्रकार के एक ही और खाली होने पर केवल एक सदस्य हो सकते हैं:

template <typename T, typename S, typename = void>
struct Empty{
    [[no_unique_address]] T t;
    [[no_unique_address]] S s;

    constexpr T& get_t() noexcept { return t; };
    constexpr S& get_s() noexcept { return s; };
};

template<typename TS>
struct Empty<TS, TS, typename std::enable_if_t<std::is_empty_v<TS>>>{
    [[no_unique_address]] TS ts;

    constexpr TS& get_t() noexcept { return ts; };
    constexpr TS& get_s() noexcept { return ts; };
};

बेशक, बाकी प्रोग्राम जो सदस्यों का उपयोग करते हैं, उन्हें उस मामले से निपटने के लिए बदलना होगा जहां केवल एक सदस्य है। इससे कोई फर्क नहीं पड़ता कि इस मामले में किस सदस्य का उपयोग किया गया है - आखिरकार, यह एक अद्वितीय वस्तु है जिसमें कोई अद्वितीय पता नहीं है। दिखाए गए सदस्य फ़ंक्शन को सरल बनाना चाहिए।

दुर्भाग्य से sizeof(Empty<Empty<A,A>,A>{})==2जहां ए पूरी तरह से खाली संरचना है।

खाली जोड़े के पुनरावर्ती संपीड़न का समर्थन करने के लिए आप और अधिक विशेषज्ञता ला सकते हैं:

template<class TS>
struct Empty<Empty<TS, TS>, TS, typename std::enable_if_t<std::is_empty_v<TS>>>{
    [[no_unique_address]] Empty<TS, TS> ts;

    constexpr Empty<TS, TS>& get_t() noexcept { return ts; };
    constexpr TS&            get_s() noexcept { return ts.get_s(); };
};

template<class TS>
struct Empty<TS, Empty<TS, TS>, typename std::enable_if_t<std::is_empty_v<TS>>>{
    [[no_unique_address]] Empty<TS, TS> ts;

    constexpr TS&            get_t() noexcept { return ts.get_t(); };
    constexpr Empty<TS, TS>& get_s() noexcept { return ts; };
};

और भी, जैसे कुछ सेक करने के लिए Empty<Empty<A, char>, A>

template <typename T, typename S>
struct Empty<Empty<T, S>, S, typename std::enable_if_t<std::is_empty_v<S>>>{
     [[no_unique_address]] Empty<T, S> ts;

    constexpr Empty<T, S>& get_t() noexcept { return ts; };
    constexpr S&           get_s() noexcept { return ts.get_s(); };
};

template <typename T, typename S>
struct Empty<Empty<S, T>, S, typename std::enable_if_t<std::is_empty_v<S>>>{
     [[no_unique_address]] Empty<S, T> st;

    constexpr Empty<S, T>& get_t() noexcept { return st; };
    constexpr S&           get_s() noexcept { return st.get_t(); };
};


template <typename T, typename S>
struct Empty<T, Empty<T, S>, typename std::enable_if_t<std::is_empty_v<T>>>{
     [[no_unique_address]] Empty<T, S> ts;

    constexpr T&           get_t() noexcept { return ts.get_t(); };
    constexpr Empty<T, S>  get_s() noexcept { return ts; };
};

template <typename T, typename S>
struct Empty<T, Empty<S, T>, typename std::enable_if_t<std::is_empty_v<T>>>{
     [[no_unique_address]] Empty<S, T> st;

    constexpr T&           get_t() noexcept { return st.get_s(); };
    constexpr Empty<S, T>  get_s() noexcept { return st; };
};

यह अच्छा है, लेकिन अभी भी दुर्भाग्य से sizeof(Empty<Empty<A,A>,A>{})==2जहां Aएक पूरी तरह से खाली struct है।
टॉम

मैं एक get_empty<T>फ़ंक्शन जोड़ूंगा। फिर आप get_empty<T>बाएं या दाएं पर फिर से उपयोग कर सकते हैं यदि यह पहले से ही वहां काम करता है।
यक्क - एडम नेवेरुमोंट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.