एक समारोह में एक std :: अज्ञात आकार पास करना


97

C ++ 11 में, मैं एक फ़ंक्शन (या विधि) लिखने के बारे में कैसे जाऊंगा जो एक std लेता है :: ज्ञात प्रकार का सरणी लेकिन अज्ञात आकार?

// made up example
void mulArray(std::array<int, ?>& arr, const int multiplier) {
    for(auto& e : arr) {
        e *= multiplier;
    }
}

// lets imagine these being full of numbers
std::array<int, 17> arr1;
std::array<int, 6>  arr2;
std::array<int, 95> arr3;

mulArray(arr1, 3);
mulArray(arr2, 5);
mulArray(arr3, 2);

अपनी खोज के दौरान मुझे केवल टेम्प्लेट का उपयोग करने के सुझाव मिले, लेकिन वे गन्दे लगते हैं (हेडर में विधि परिभाषाएँ) और जो मैं पूरा करने की कोशिश कर रहा हूं उसके लिए अत्यधिक।

क्या यह काम करने का एक सरल तरीका है, जैसा कि सादे सी-स्टाइल सरणियों के साथ होगा?


1
Arrays की कोई जाँच नहीं है या पता नहीं है कि वे किस आकार के हैं। इसलिए, आपको उन्हें किसी चीज़ में लपेटना चाहिए या उपयोग पर विचार करना चाहिए std::vector
ट्रैविस पेसेटो

19
यदि टेम्पलेट आपको गन्दा और अत्यधिक लगता है, तो आपको उस भावना पर काबू पा लेना चाहिए। वे C ++ में आम हैं।
बेंजामिन लिंडले

std::vector@TravisPessetto के रूप में उपयोग करने का कोई कारण नहीं सुझाता है?
कॉरी क्लेन

2
समझ लिया। अगर यह उनके स्वभाव की सीमा है, तो मुझे यह स्वीकार करना होगा। कारण मैंने std से बचने के बारे में सोचा :: वेक्टर (जो मेरे लिए बहुत अच्छा काम करता है), यह है कि यह ढेर पर आवंटित किया गया है। चूँकि ये सरणियाँ छोटी होंगी और कार्यक्रम के प्रत्येक पुनरावृत्ति में बदल जाएंगी, मैंने सोचा कि एक std :: array थोड़ा बेहतर प्रदर्शन कर सकती है। मुझे लगता है कि मैं एक सी-शैली सरणी का उपयोग करूंगा, मेरा कार्यक्रम जटिल नहीं है।
एड्रियन

15
@ एड्रियन आपके प्रदर्शन के बारे में सोचने का तरीका पूरी तरह से गलत है। इससे पहले कि आप एक कार्यात्मक कार्यक्रम भी करें, सूक्ष्म अनुकूलन करने का प्रयास न करें। और आपके पास एक कार्यक्रम होने के बाद, यह अनुमान न लगाएं कि क्या अनुकूलित किया जाना चाहिए, इसके बजाय एक प्रोफाइलर आपको बताता है कि कार्यक्रम के किस हिस्से को अनुकूलित किया जाना चाहिए।
पॉल मंटा

जवाबों:


86

क्या यह काम करने का एक सरल तरीका है, जैसा कि सादे सी-स्टाइल सरणियों के साथ होगा?

नहीं। आप वास्तव में ऐसा नहीं कर सकते जब तक कि आप अपने फ़ंक्शन को फ़ंक्शन टेम्प्लेट नहीं बनाते हैं (या किसी अन्य प्रकार के कंटेनर का उपयोग करें, जैसे कि std::vector, जैसा कि प्रश्न में टिप्पणी में सुझाया गया है):

template<std::size_t SIZE>
void mulArray(std::array<int, SIZE>& arr, const int multiplier) {
    for(auto& e : arr) {
        e *= multiplier;
    }
}

यहाँ एक जीवंत उदाहरण है


7
ओपी पूछता है कि क्या कोई और समाधान है लेकिन टेम्पलेट।
नोवाक

1
@ एड्रियन: दुर्भाग्य से कोई अन्य समाधान नहीं है, यदि आप चाहते हैं कि आपका कार्य किसी भी आकार के सरणियों पर उदारता से काम करे ...
एंडी प्रोल

1
सही: कोई दूसरा तरीका नहीं है। चूंकि प्रत्येक एसटीडी :: एक अलग आकार के साथ सरणी एक अलग प्रकार है, आपको एक फ़ंक्शन लिखने की ज़रूरत है जो विभिन्न प्रकारों पर काम कर सकती है। इसलिए, टेम्पलेट std :: array के लिए समाधान हैं।
बस्तमौर

4
यहाँ एक टेम्पलेट का उपयोग करने के बारे में सुंदर हिस्सा यह है कि आप इसे और भी सामान्य बना सकते हैं, ताकि यह किसी भी अनुक्रम कंटेनर के साथ-साथ मानक सरणियों के साथ काम करे:template<typename C, typename M> void mulArray(C & arr, M multiplier) { /* same body */ }
बेंजामिन लिंडले

1
@BenjaminLindley: बेशक, यह मानता है कि वह कोड को हेडर में डाल सकता है।
निकोल बोलस

25

का आकार प्रकारarray का हिस्सा है , इसलिए आप वह नहीं कर सकते जो आप चाहते हैं। कुछ विकल्प हैं।

पसंदीदा पुनरावृत्तियों की एक जोड़ी लेने के लिए होगा:

template <typename Iter>
void mulArray(Iter first, Iter last, const int multiplier) {
    for(; first != last; ++first) {
        *first *= multiplier;
    }
}

वैकल्पिक रूप से, vectorसरणी के बजाय का उपयोग करें , जो आपको इसके प्रकार के भाग के बजाय रनटाइम पर आकार को संग्रहीत करने की अनुमति देता है:

void mulArray(std::vector<int>& arr, const int multiplier) {
    for(auto& e : arr) {
        e *= multiplier;
    }
}

1
मुझे लगता है कि यह बेहतर उपाय है; यदि आप एक टेम्पलेट बनाने की परेशानी से गुजरने जा रहे हैं, तो इसे पुनरावृत्तियों के साथ पूरी तरह से सामान्य बना दें जो आपको किसी भी कंटेनर (सरणी, सूची, वेक्टर, यहां तक ​​कि पुराने स्कूल सी पॉइंटर्स, आदि) का उपयोग बिना किसी नकारात्मक पक्ष के करने देगा। संकेत के लिए धन्यवाद।
मार्क लकाटा

5

मैंने नीचे कोशिश की और यह मेरे लिए काम कर गया।

#include <iostream>
#include <array>

using namespace std;

// made up example
void mulArray(auto &arr, const int multiplier) 
{
    for(auto& e : arr) 
    {
        e *= multiplier;
    }
}

void dispArray(auto &arr)
{
    for(auto& e : arr) 
    {
        std::cout << e << " ";
    }
    std::cout << endl;
}

int main()
{

    // lets imagine these being full of numbers
    std::array<int, 7> arr1 = {1, 2, 3, 4, 5, 6, 7};
    std::array<int, 6> arr2 = {2, 4, 6, 8, 10, 12};
    std::array<int, 9> arr3 = {1, 1, 1, 1, 1, 1, 1, 1, 1};

    dispArray(arr1);
    dispArray(arr2);
    dispArray(arr3);

    mulArray(arr1, 3);
    mulArray(arr2, 5);
    mulArray(arr3, 2);

    dispArray(arr1);
    dispArray(arr2);
    dispArray(arr3);

    return 0;
}

OUTPUT:

1 2 3 4 5 6 7

२ ४ ६ 8 १० १२

१ १ १ १ १ १ १ १ १ १ १

३ ६ ९ १२ १५ १ 18 २१

10 20 30 40 50 60

2 2 2 2 2 2 2 2 2


2
यह C ++ मान्य नहीं है, बल्कि एक एक्सटेंशन है। ये फ़ंक्शंस टेम्पलेट हैं, बिना भी template
होलीब्लैककैट

मैंने इस पर ध्यान दिया है और ऐसा प्रतीत होता है कि auto foo(auto bar) { return bar * 2; }वर्तमान में C ++ मान्य नहीं है, भले ही यह CCC 17 फ्लैग सेट के साथ GCC7 में संकलित हो। यहां पढ़ने से , ऑटो के रूप में घोषित फ़ंक्शन पैरामीटर कॉन्सेप्ट टीएस का हिस्सा हैं जो अंततः सी ++ 20 का हिस्सा होना चाहिए।
फाइबल्स

चेतावनी C26485
मेटाबस्टर

5

संपादित करें

C ++ 20 में अस्थायी रूप से शामिल हैं std::span

https://en.cppreference.com/w/cpp/container/span

मूल उत्तर

आप जो चाहते हैं वह कुछ ऐसा है gsl::span, जो C ++ कोर दिशानिर्देशों में वर्णित दिशानिर्देश समर्थन लाइब्रेरी में उपलब्ध है:

https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#SS-views

आप यहाँ एक ओपन सोर्स हेडर जीएसएल का केवल कार्यान्वयन पा सकते हैं:

https://github.com/Microsoft/GSL

इसके साथ gsl::span, आप यह कर सकते हैं:

// made up example
void mulArray(gsl::span<int>& arr, const int multiplier) {
    for(auto& e : arr) {
        e *= multiplier;
    }
}

// lets imagine these being full of numbers
std::array<int, 17> arr1;
std::array<int, 6>  arr2;
std::array<int, 95> arr3;

mulArray(arr1, 3);
mulArray(arr2, 5);
mulArray(arr3, 2);

इसके साथ समस्या std::arrayयह है कि इसका आकार इसके प्रकार का हिस्सा है, इसलिए आपको एक ऐसे कार्य को कार्यान्वित करने के लिए टेम्पलेट का उपयोग करना होगा जो एक std::arrayमनमाना आकार लेता है ।

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

std::vector<int> vec = {1, 2, 3, 4};
int carr[] = {5, 6, 7, 8};

mulArray(vec, 6);
mulArray(carr, 7);

बहुत अच्छा, हुह?


3

बिल्कुल, C ++ 11 में एक फ़ंक्शन लिखने के लिए एक सरल तरीका है जो ज्ञात प्रकार का एक std :: array लेता है, लेकिन अज्ञात आकार।

अगर हम एरे साइज़ को फंक्शन में पास नहीं कर पा रहे हैं, तो इसके बजाय, हम मेमोरी एड्रेस को पास कर सकते हैं, जहाँ ऐरे के 2 एड्रेस के साथ एरे शुरू होता है, जहाँ ऐरे की समाप्ति होती है। बाद में, फ़ंक्शन के अंदर, हम सरणी के आकार की गणना करने के लिए इन 2 मेमोरी पतों का उपयोग कर सकते हैं!

#include <iostream>
#include <array>

// The function that can take a std::array of any size!
void mulArray(int* piStart, int* piLast, int multiplier){

     // Calculate the size of the array (how many values it holds)
     unsigned int uiArraySize = piLast - piStart;

     // print each value held in the array
     for (unsigned int uiCount = 0; uiCount < uiArraySize; uiCount++)     
          std::cout << *(piStart + uiCount) * multiplier << std::endl;
}

int main(){   

     // initialize an array that can can hold 5 values
     std::array<int, 5> iValues;

     iValues[0] = 5;
     iValues[1] = 10;
     iValues[2] = 1;
     iValues[3] = 2;
     iValues[4] = 4;

     // Provide a pointer to both the beginning and end addresses of 
     // the array.
     mulArray(iValues.begin(), iValues.end(), 2);

     return 0;
}

कंसोल पर आउटपुट: 10, 20, 2, 4, 8


1

यह किया जा सकता है, लेकिन इसे साफ करने के लिए कुछ कदम उठाए जाते हैं। सबसे पहले, एक लिखें जो template classसन्निहित मूल्यों की एक श्रृंखला का प्रतिनिधित्व करता है। फिर एक templateसंस्करण को आगे बढ़ाएं जो जानता arrayहै Implकि इस सन्निहित सीमा को लेने वाले संस्करण के लिए कितना बड़ा है ।

अंत में, contig_rangeसंस्करण को लागू करें । ध्यान दें कि for( int& x: range )के लिए काम करता contig_rangeहै, क्योंकि मैं लागू किया begin()और end()और संकेत iterators हैं।

template<typename T>
struct contig_range {
  T* _begin, _end;
  contig_range( T* b, T* e ):_begin(b), _end(e) {}
  T const* begin() const { return _begin; }
  T const* end() const { return _end; }
  T* begin() { return _begin; }
  T* end() { return _end; }
  contig_range( contig_range const& ) = default;
  contig_range( contig_range && ) = default;
  contig_range():_begin(nullptr), _end(nullptr) {}

  // maybe block `operator=`?  contig_range follows reference semantics
  // and there really isn't a run time safe `operator=` for reference semantics on
  // a range when the RHS is of unknown width...
  // I guess I could make it follow pointer semantics and rebase?  Dunno
  // this being tricky, I am tempted to =delete operator=

  template<typename T, std::size_t N>
  contig_range( std::array<T, N>& arr ): _begin(&*std::begin(arr)), _end(&*std::end(arr)) {}
  template<typename T, std::size_t N>
  contig_range( T(&arr)[N] ): _begin(&*std::begin(arr)), _end(&*std::end(arr)) {}
  template<typename T, typename A>
  contig_range( std::vector<T, A>& arr ): _begin(&*std::begin(arr)), _end(&*std::end(arr)) {}
};

void mulArrayImpl( contig_range<int> arr, const int multiplier );

template<std::size_t N>
void mulArray( std::array<int, N>& arr, const int multiplier ) {
  mulArrayImpl( contig_range<int>(arr), multiplier );
}

(परीक्षण नहीं, लेकिन डिजाइन काम करना चाहिए)।

फिर, आपकी .cppफ़ाइल में:

void mulArrayImpl(contig_range<int> rng, const int multiplier) {
  for(auto& e : rng) {
    e *= multiplier;
  }
}

इसका नकारात्मक पक्ष यह है कि सरणी की सामग्री पर लूप कोड कोड (संकलन समय पर) नहीं जानता कि सरणी कितना बड़ा है, जो कि अनुकूलन को खर्च कर सकता है। इसका यह फायदा है कि कार्यान्वयन हेडर में नहीं होता है।

स्पष्ट रूप से निर्माण के बारे में सावधान रहें contig_range, जैसे कि आप इसे पास करते हैं, setयह मान लेगा कि setडेटा सन्निहित है, जो गलत है, और सभी जगह अपरिभाषित व्यवहार करते हैं। केवल दो stdकंटेनरों कि यह काम करने की गारंटी है vectorऔर array(और सी-स्टाइल सरणियों, जैसा कि ऐसा होता है!)। dequeयादृच्छिक अभिगम होने के बावजूद यह खतरनाक नहीं है (खतरनाक रूप से, यह छोटे टुकड़ों में सन्निहित है!), listकरीब भी नहीं है, और साहचर्य (आदेशित और अव्यवस्थित) कंटेनर समान रूप से गैर-संक्रामक हैं।

इसलिए जिन तीन कंस्ट्रक्टरों को मैंने लागू किया है std::array, वे हैं, std::vectorऔर सी-स्टाइल सरणियाँ, जो मूल रूप से आधारों को कवर करती हैं।

लागू []करना आसान है, और बीच में है for()और []जो आप के लिए सबसे ज्यादा चाहते हैं array, है ना?


क्या यह सिर्फ कहीं और टेम्पलेट की भरपाई नहीं है?
GManNickG

@GManNickG की तरह। हेडर को वास्तव में templateकोई फ़ंक्शन विवरण नहीं होने के साथ एक छोटा फ़ंक्शन मिलता है । Implसमारोह एक नहीं है templateसमारोह, और इसलिए आप खुशी से में कार्यान्वयन छिपा कर सकते हैं .cppअपनी पसंद की फ़ाइल। यह प्रकार विलोपन का एक बहुत कच्चे तेल की तरह है, जहां मैं एक सरल वर्ग में सन्निहित कंटेनरों पर पुनरावृति करने की क्षमता को निकालने, और उसके बाद से पारित है कि के माध्यम से ... (जबकि multArrayImplएक लेता है templateएक तर्क के रूप में, यह एक नहीं है templateही)।
यक्क - एडम नेवरामॉन्ट

मैं समझता हूं कि यह सरणी दृश्य / सरणी प्रॉक्सी वर्ग कभी-कभी उपयोगी होता है। मेरा सुझाव कंस्ट्रक्टर में कंटेनर के आरंभ / अंत को पारित करना होगा ताकि आपको प्रत्येक कंटेनर के लिए एक कंस्ट्रक्टर लिखना न पड़े। इसके अलावा मैं '* और std :: start (arr)' को dereferencing के रूप में नहीं लिखूंगा और पता लेने के लिए यहाँ अनावश्यक है: std :: start / std :: end पहले से ही एक पुनरावृत्तिकर्ता को लौटाता है।
रिकी 65

@ Ricky65 यदि आप पुनरावृत्तियों का उपयोग करते हैं, तो आपको कार्यान्वयन को उजागर करने की आवश्यकता है। यदि आप पॉइंटर्स का उपयोग करते हैं, तो आप नहीं करते हैं। &*Dereferences iterator (जो एक सूचक नहीं हो सकता है), तो पता करने के लिए एक सूचक बना देता है। सन्निहित मेमोरी डेटा के लिए, पॉइंटर टू beginऔर पॉइंटर टू वन-पास्ट endभी यादृच्छिक अभिगमकर्ता हैं, और वे एक प्रकार से अधिक प्रत्येक सन्निहित रेंज के लिए एक ही प्रकार हैं T
यक्क - एडम नेवरुमोंट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.