टेम्पलेट कक्षाओं की पीढ़ी के लिए लूप में एक कांस्टेबल चर कैसे है?


15

मेरे पास एक कोड है

template <size_t N>
class A
{
    template <size_t N>
    someFunctions() {};
};

अब मैं कक्षा के उदाहरण बनाना चाहता हूं और इसमें कई मानों के एक सेट के लिए लूप में फ़ंक्शन को कॉल करना चाहता हूं

// in main()

int main()
{
    for (int i = 1; i <= 100; i++)
    {
        const int N = i;  // dont know how to do this
        A<N> a;
        a.functionCalls();
    }
}

यह कैसे करना है? ऐसा करने के लिए एक विधि की उम्मीद है।


टेम्पलेट पैरामीटर के रूप में उपयोग किए जाने के लिए ऐसा होना Nचाहिए constexprजो यदि लूप वैरिएबल है तो ऐसा नहीं है
CoryKramer

आप नहीं कर सकते, क्या वास्तव में एक टेम्पलेट होने की आवश्यकता है?
एलन बर्टल्स

हाँ, कुछ कारणों के लिए क्लास ए की आवश्यकता है और यह कुछ का एक मॉडल है, इसलिए इसे एक टेम्पलेट क्लास होना चाहिए
nachiappan venkatesh

जवाबों:


11

इसके लिए किसी ऐसी चीज की आवश्यकता होगी, जिसे template forअपेक्षित रूप में विस्तार विवरण कहा जाएगा, जो कि लूप के लिए दिखने वाली चीज है, लेकिन वास्तव में एक फंक्शन में एक टेम्प्लेटेड ब्लॉक होता है जिसे कई बार इंस्टेंट किया जाता है।

बेशक, एक वर्कअराउंड है। हम किसी भी प्रकार के स्थानीय टेम्पलड ब्लॉक को घोषित करने के लिए सामान्य लाम्बदास का दुरुपयोग कर सकते हैं और इसे हमारे ऊपर उकेर सकते हैं:

template <typename T, T... S, typename F>
constexpr void for_sequence(std::integer_sequence<T, S...>, F f) {
    (static_cast<void>(f(std::integral_constant<T, S>{})), ...);
}

यह फ़ंक्शन पूर्णांक अनुक्रम लेता है और लंबोदा अनुक्रम Fकी लंबाई के रूप में कई बार त्वरित करता है।

इसका उपयोग इस तरह किया जाता है:

for_sequence(std::make_index_sequence<100>(), [](auto N) { /* N is from 0 to 99 */
  A<N + 1> a; /* N + 1 is from 1 to 100 */
  a.functionCalls();
});

यहाँ, Nटेम्पलेट पैरामीटर के रूप में भेजा जा सकता है क्योंकि यह एक ऑब्जेक्ट है जिसमें एक पूर्णांक रूपांतरण ऑपरेटर एक पूर्णांक प्रकार के लिए है। अधिक सटीक रूप से, यह std::integral_constantबढ़ते मूल्य के साथ है।

जीवंत उदाहरण


3
ओह। जब मुझे इस तरह से टेम्प्लेट का मज़ा आता है, तो मुझे पता है कि मैं इसे बिना कॉलस्टैक के बाद में डिबग करने जा रहा हूं और यह अनुमान लगाना होगा कि क्या चल रहा है ... :)
माइकल डोरगन

का उद्देश्य क्या है static_cast<void>?
अयक्शण

2
@ एयक्सन समस्याओं से बचता है जब लैम्ब्डा fएक प्रकार देता है जो अल्पविराम ऑपरेटर को
अधिभारित करता है

@MichaelDorgan यही कारण है कि हम की जरूरत है template for। इस तरह से गाली देने से भाषा का निर्माण हमेशा अधिक दर्दनाक होता है
गिलियूम रेसिकॉट

@GuillaumeRacicot या हमें मेटा प्रोग्रामिंग के लिए टेम्प्लेट की तुलना में बेहतर अमूर्तता की आवश्यकता है।
अजय ब्रह्मक्षत्रिय

5

Nसंकलन-समय स्थिर रहने की जरूरत है, जो एक सामान्य forलूप के साथ संभव नहीं है।

लेकिन, कई वर्कअराउंड हैं। उदाहरण के लिए, इस एसओ पद से प्रेरित होकर , आप निम्न की तरह कुछ कर सकते हैं। ( एक लाइव डेमो देखें )

template<size_t N>
class A
{
public:
    // make the member function public so that you can call with its instance
    void someFunctions()
    {
        std::cout << N << "\n";
    };
};

template<int N> struct AGenerator
{
    static void generate()
    {
        AGenerator<N - 1>::generate();
        A<N> a;
        a.someFunctions();
    }
};

template<> struct AGenerator<1>
{
    static void generate()
    {
        A<1> a;
        a.someFunctions();
    }
};

int main()
{
    // call the static member for constructing 100 A objects
    AGenerator<100>::generate();
}

प्रिंट 1करता है100


में , ऊपर एक भी टेम्पलेट करने के लिए कम किया जा सकता है AGeneratorवर्ग (यानी विशेषज्ञता बचा जा सकता है), का उपयोग करते हुए if constexpr( एक लाइव डेमो देखें )

template<std::size_t N>
struct AGenerator final
{
    static constexpr void generate() noexcept
    {
        if constexpr (N == 1)
        {
            A<N> a;
            a.someFunctions();
            // .. do something more with `a`
        }
        else
        {
            AGenerator<N - 1>::generate();
            A<N> a;
            a.someFunctions();
            // .. do something more with `a`
        }
    }
};

आउटपुट :

1
2
3
4
5
6
7
8
9
10

पुनरावृति की श्रेणी प्रदान करने के मामले में, आप निम्नलिखित का उपयोग कर सकते हैं। ( एक लाइव डेमो देखें )

template<std::size_t MAX, std::size_t MIN = 1> // `MIN` is set to 1 by default
struct AGenerator final
{
    static constexpr void generate() noexcept
    {
        if constexpr (MIN == 1)
        {
            A<MIN> a;
            a.someFunctions();
            // .. do something more with `a`
            AGenerator<MAX, MIN + 1>::generate();
        }
        else if constexpr (MIN != 1 && MIN <= MAX)
        {
            A<MIN> a;
            a.someFunctions();
            // .. do something more with `a`
            AGenerator<MAX, MIN + 1>::generate();
        }
    }
};

int main()
{
    // provide the `MAX` count of looping. `MIN` is set to 1 by default
    AGenerator<10>::generate();
}

उपरोक्त संस्करण के समान ही आउटपुट।


4

C ++ 20 से, आप टेम्पलेट लैम्ब्डा का उपयोग कर सकते हैं, इसलिए आप कुछ इस प्रकार की कोशिश कर सकते हैं

[]<int ... Is>(std::integer_sequence<int, Is...>)
 { (A<Is+1>{}.functionCall(), ...); }
   (std::make_integer_sequence<int, 100>{});

निम्नलिखित एक पूर्ण संकलन उदाहरण है जो सभी संख्याओं को 0 से 99 तक प्रिंट करता है

#include <utility>
#include <iostream>

int main()
 {
  []<int ... Is>(std::integer_sequence<int, Is...>)
   { (std::cout << Is << std::endl, ...); }
     (std::make_integer_sequence<int, 100>{});
 }

1

ऐसा करने का एक तरीका टेम्पलेट मेटा-प्रोग्रामिंग के साथ कुछ इस तरह से है:

#include <iostream>

template <std::size_t N>
struct A {
  void foo() { std::cout << N << '\n'; }
};

template <std::size_t from, std::size_t to>
struct call_foo {
  void operator()() {
    if constexpr (from != to) {
      A<from + 1>{}.foo();
      call_foo<from + 1, to>{}();
    }
  }
};

int main() { call_foo<0, 100>{}(); }

0

बस पूर्णता के लिए - क्या यह वास्तव में वर्ग या फ़ंक्शन के लिए आवश्यक है, यदि फ़ंक्शन का एकमात्र उपयोग लूप से बुलाया जाना है?

यदि ऐसा है और आप हाथ से लिखने को बढ़ावा देना नहीं चाहते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.