कुल आरंभ के लिए खाली आधार वर्ग छिपाएँ


9

निम्नलिखित कोड पर विचार करें:

struct A
{
    // No data members
    //...
};

template<typename T, size_t N>
struct B : A
{
    T data[N];
}

इस तरह आपको B को इनिशियलाइज़ B<int, 3> b = { {}, {1, 2, 3} }; करना है : मैं बेस क्लास के लिए अनावश्यक खाली {} से बचना चाहता हूँ। Jarod42 द्वारा यहां एक समाधान प्रस्तावित किया गया है , हालांकि, यह तत्वों के साथ काम नहीं करता है डिफ़ॉल्ट आरंभीकरण: B<int, 3> b = {1, 2, 3};ठीक है, लेकिन B<int, 3> b = {1};नहीं है: b.data[1]और b.data[2]डिफ़ॉल्ट 0 से आरंभिक नहीं है, और एक संकलक त्रुटि होती है। क्या निर्माण से बेस क्लास को "छिपाने" के लिए कोई रास्ता है (या सी ++ 20 के साथ होगा)?


2
कंस्ट्रक्टर क्यों नहीं जोड़ना है template<class... Ts> B(Ts... args) : data{args...} {}?
Evg

यह एक टिप्पणी क्यों है? यह काम करने लगता है, lol
user7769147

यह एक ऐसा स्पष्ट समाधान है जो मुझे लगा कि आपके पास इसका उपयोग न करने का कोई कारण है। :)
Evg

यह बहुत आसान एक्सडी था। यदि आप इसे एक उत्तर के रूप में लिखते हैं, तो मैं इसे स्वीकार करूंगा
user7769147

जवाबों:


6

सबसे आसान समाधान एक वैराडिक कंस्ट्रक्टर को जोड़ना है:

struct A { };

template<typename T, std::size_t N>
struct B : A {
    template<class... Ts, typename = std::enable_if_t<
        (std::is_convertible_v<Ts, T> && ...)>>
    B(Ts&&... args) : data{std::forward<Ts>(args)...} {}

    T data[N];
};

void foo() {
    B<int, 3> b1 = {1, 2, 3};
    B<int, 3> b2 = {1};
}

यदि आप {...}आरंभिक सूची की तुलना में कम तत्व प्रदान करते हैं N, तो सरणी में शेष तत्व dataमान के अनुसार आरंभिक हो जाएंगे T()


3
मैंने अभी-अभी पता लगाया है कि यह एग्रीगेट इनिशियलाइज़ेशन से अलग क्यों है। यदि आप विचार B<Class, 5> b = {Class()}; Classकरते हैं कि पहले निर्माण किया जाएगा और फिर स्थानांतरित किया जाएगा, जबकि कुल उपयोग से Classनिर्माण शुरू होगा, कोई चाल शामिल नहीं है
user7769147

@ user7769147, अच्छी बात है। आप std::tupleतर्क का सहारा ले सकते हैं और उनका उपयोग वस्तुओं को जगह बनाने के लिए कर सकते हैं। लेकिन वाक्य रचना बोझिल होगी।
Evg

1
मैंने अनियमित रूप से एक समाधान पाया है जो इस समस्या को हल करता है, मैं इसे आपकी उपलब्धता के लिए धन्यवाद करने के लिए स्वीकृत उत्तर के रूप में छोड़ने जा रहा हूं :)।
user7769147

4

C ++ 20 के बाद से आप एग्रीगेट इनिशियलाइज़ेशन में निर्दिष्ट इनिशियलाइज़र का उपयोग कर सकते हैं ।

B<int, 3> b = { .data {1} }; // initialize b.data with {1}, 
                             // b.data[0] is 1, b.data[1] and b.data[2] would be 0

यह अभी भी मेरे लिए क्रिया है, यह एक न्यूनतम उदाहरण था। मेरे सरणी सदस्य का एक अजीब नाम है जिसे उपयोगकर्ता द्वारा अनदेखा किया जाना चाहिए
user7769147

4

अभी भी कंस्ट्रक्टर के साथ, आप कुछ ऐसा कर सकते हैं:

template<typename T, size_t N>
struct B : A
{
public:
    constexpr B() : data{} {}

    template <typename ... Ts,
              std::enable_if_t<(sizeof...(Ts) != 0 && sizeof...(Ts) < N)
                               || !std::is_same_v<B, std::decay_t<T>>, int> = 0>
    constexpr B(T&& arg, Ts&&... args) : data{std::forward<T>(arg), std::forward<Ts>(args)...}
    {}

    T data[N];
};

डेमो

SFINAE मुख्य रूप से छद्म कॉपी कंस्ट्रक्टर बनाने से बचने के लिए किया जाता है B(B&)

समर्थन के लिए आपको अतिरिक्त निजी टैग की आवश्यकता होगी B<std::index_sequence<0, 1>, 42>;-)


आपको आवश्यकता क्यों है ((void)Is, T())...? यदि आप इसे छोड़ देते हैं तो क्या होगा? क्या शेष तत्व T()डिफ़ॉल्ट रूप से मूल्य-आरंभ नहीं होंगे ?
Evg

1
@ ईवीजी: वास्तव में, सरलीकृत। केवल डिफ़ॉल्ट तत्वों के बजाय मूल्य को इनिशियलाइज़ करने के लिए उन्हें डिफ़ॉल्ट करने से डर लगता था ...
Jarod42

2

मुझे एक और समाधान मिला है (मुझे नहीं पता कि कैसे) पूरी तरह से काम करता है और उस समस्या को हल करता है जिसे हम Evg के उत्तर के तहत चर्चा कर रहे थे

struct A {};

template<typename T, size_t N>
struct B_data
{
    T data[N];
};

template<typename T, size_t N>
struct B : B_data<T, N>, A
{
    // ...
};

दिलचस्प समाधान। लेकिन अब एक का उपयोग करना है this->dataया अंदर using B_data::data;पहुंचना dataहै B
Evg
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.