কোনও ফাংশনে অজানা আকারের একটি স্টাডি :: অ্যারে পাস করা


102

সি ++ ১১-এ, আমি কীভাবে একটি ফাংশন (বা পদ্ধতি) লিখতে যাব যে একটি স্ট্যান্ড :: জ্ঞাত প্রকারের অ্যারে নেবে কিন্তু অজানা আকারের?

// 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);

আমার অনুসন্ধানের সময় আমি কেবলমাত্র টেমপ্লেটগুলি ব্যবহার করার জন্য পরামর্শ পেয়েছি, তবে এগুলি অগোছালো বলে মনে হচ্ছে (শিরোনামে পদ্ধতির সংজ্ঞা) এবং আমি কী সম্পাদন করার চেষ্টা করছি তার জন্য অতিরিক্ত।

এই কাজটি করার সহজ উপায় কি আছে, যেমন একসাথে সি-স্টাইলের অ্যারে রয়েছে?


4
অ্যারেগুলির কোনও চেক করার সীমা নেই বা তারা কোন আকারের তা জানে। অতএব, আপনাকে অবশ্যই এগুলি কোনও কিছুতে মুড়িয়ে রাখতে হবে বা ব্যবহার বিবেচনা করতে হবে std::vector
ট্র্যাভিস পেসেট্টো

20
যদি টেম্পলেটগুলি আপনার কাছে অগোছালো এবং অত্যধিক বলে মনে হয়, তবে আপনার সেই অনুভূতিটি হওয়া উচিত। এগুলি সি ++ এ সাধারণ।
বেনজামিন লিন্ডলি

std::vector@ ট্র্যাভিসপেসেটো প্রস্তাবিত হিসাবে ব্যবহার না করার কোনও কারণ ?
Cory Klein

4
বুঝেছি। এটি যদি তাদের প্রকৃতির সীমাবদ্ধতা থাকে তবে আমাকে তা গ্রহণ করতে হবে। যে কারণে আমি স্টাড :: ভেক্টর (যা আমার পক্ষে দুর্দান্ত কাজ করে) এড়ানোর বিষয়ে ভেবেছিল তা হ'ল এটি স্তূপে বরাদ্দ। প্রোগ্রামগুলির প্রতিটি পুনরাবৃত্তিতে এই অ্যারেগুলি ছোট এবং লুপ করা হবে বলে আমি ভেবেছিলাম একটি স্টাড :: অ্যারে কিছুটা আরও ভাল পারফর্ম করতে পারে। আমি মনে করি আমি তখন সি-স্টাইলের অ্যারে ব্যবহার করব, আমার প্রোগ্রামটি জটিল নয়।
অ্যাড্রিয়ান

15
@ অ্যাড্রিয়ান আপনার অভিনয় সম্পর্কে চিন্তাভাবনা সম্পূর্ণ ভুল। এমনকি আপনার কোনও কার্যকরী প্রোগ্রাম হওয়ার আগে মাইক্রো অপ্টিমাইজেশান তৈরি করার চেষ্টা করবেন না। এবং আপনার কোনও প্রোগ্রাম হওয়ার পরে, কীটি অপ্টিমাইজ করা উচিত তা সম্পর্কে অনুমান করবেন না , পরিবর্তে কোনও প্রোফাইলার আপনাকে প্রোগ্রামের কোন অংশটি অনুকূলিত করা উচিত তা জানান।
পল মানতা

উত্তর:


90

এই কাজটি করার সহজ উপায় কি আছে, যেমন একসাথে সি-স্টাইলের অ্যারে রয়েছে?

না। আপনি যদি নিজের ফাংশনটিকে কোনও ফাংশন টেম্পলেট না করে থাকেন তবে (বা std::vectorপ্রশ্নের মন্তব্যে প্রস্তাবিত কোনও ধরণের ধারক ব্যবহার না করা) আপনি আসলেই এটি করতে পারবেন না :

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

এখানে একটি সরাসরি উদাহরণ


9
টেমপ্লেট ছাড়াও অন্য কোনও সমাধান আছে কিনা জানতে ওপি জিজ্ঞাসা করে।
নভাক

4
@ অ্যাড্রিয়ান: দুর্ভাগ্যক্রমে অন্য কোনও সমাধান নেই, যদি আপনি চান যে কোনও আকারের অ্যারেতে আপনার ফাংশনটি উদারভাবে কাজ করতে পারে ...
অ্যান্ডি প্রল

4
সঠিক: অন্য কোনও উপায় নেই। যেহেতু প্রতিটি স্ট্যান্ড :: অ্যারে বিভিন্ন আকারের একটি আলাদা ধরণের, তাই আপনাকে একটি ফাংশন লিখতে হবে যা বিভিন্ন ধরণের কাজ করতে পারে। সুতরাং, টেমপ্লেটগুলি std :: অ্যারের সমাধান for
bstamour

4
এখানে একটি টেমপ্লেট ব্যবহার করার সুন্দর অংশটি হ'ল আপনি এটিকে আরও জেনেরিক তৈরি করতে পারেন, যাতে এটি কোনও ক্রম ধারক, পাশাপাশি মানক অ্যারেগুলির সাথে কাজ করে:template<typename C, typename M> void mulArray(C & arr, M multiplier) { /* same body */ }
বেনজমিন লিন্ডলি

4
@ অ্যান্ডিপ্রল লাইভ উদাহরণ লিঙ্কটি আর কাজ করে না
আহমদ হুসেন

28

আকার 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;
    }
}

4
আমি মনে করি এটিই সর্বোত্তম সমাধান; আপনি যদি কোনও টেমপ্লেট তৈরির সমস্যায় পড়তে চলেছেন তবে এটির পুনরাবৃত্তিকারীদের সাথে একেবারে জেনেরিক করুন যা আপনাকে কোনও কন্টেইনার (অ্যারে, তালিকা, ভেক্টর এমনকি পুরানো স্কুল সি পয়েন্টার ইত্যাদি) ব্যবহার করতে দেবে। ইঙ্গিতটির জন্য ধন্যবাদ।
লাকাতা

9

সম্পাদনা

সি ++ 20 অন্তর্ভুক্ত অন্তর্ভুক্ত std::span

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

আসল উত্তর

আপনি যা চান তা হ'ল এটি gsl::span, যা সি ++ কোর গাইডলাইনে বর্ণিত গাইডলাইন সহায়তা লাইব্রেরিতে পাওয়া যায়:

https://github.com/isocpp/CppCoreGuidlines/blob/master/CppCoreGuidlines.md#SS- ভিউ

আপনি এখানে জিএসএল-এর একটি মুক্ত-উত্স শিরোনাম-কেবল বাস্তবায়ন পেতে পারেন:

https://github.com মাইক্রোসফট / জিএসএল

সহ 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);

খুব সুন্দর, হাহ?


6

আমি নীচে চেষ্টা করেছি এবং এটি কেবল আমার জন্য কাজ করেছে।

#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;
}

আউটপুট:

1 2 3 4 5 6 7

2 4 6 8 10 12

1 1 1 1 1 1 1 1 1 1

3 6 9 12 15 18 21

10 20 30 40 50 60

2 2 2 2 2 2 2 2 2 2


4
এটি বৈধ সি ++ নয়, বরং একটি এক্সটেনশন। এই ফাংশনগুলি টেমপ্লেটগুলি, এমনকি ছাড়াই template
হলি ব্ল্যাককিট 14'18

4
আমি এটি সন্ধান করেছি এবং এটি প্রদর্শিত হয় যে auto foo(auto bar) { return bar * 2; }এটি বর্তমানে বৈধ সি ++ নয় যদিও এটি জিসিসি 7 এ সি ++ 17 পতাকা সেট সহ সংকলন করে। এখানে পড়া থেকে , অটো হিসাবে ঘোষিত ফাংশন প্যারামিটারগুলি ধারণার টিএস এর অংশ যা শেষ পর্যন্ত সি ++ 20 এর অংশ হওয়া উচিত।
ফিবলস

সতর্কবাণী C26485
metablaster

3

অবশ্যই, একটি ফাংশন লিখতে সি ++ 11 এ একটি সহজ উপায় রয়েছে যা পরিচিত ধরণের স্ট্যান্ড :: অ্যারে নেবে তবে অজানা আকার।

যদি আমরা ফাংশনে অ্যারের আকারটি পাস করতে না পারি তবে তার পরিবর্তে, আমরা অ্যারে যেখানে শেষ হয় তার ২ য় ঠিকানা সহ অ্যারে শুরু হয় তার মেমরি ঠিকানাটি পাস করতে পারি। পরে, ফাংশনের অভ্যন্তরে, আমরা অ্যারেটির আকার গণনা করতে এই 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()এবং পয়েন্টারগুলি পুনরাবৃত্তকারী।

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ডেটাটি সংলগ্ন, যা মিথ্যা, এবং সমস্ত জায়গায় অপরিজ্ঞাত আচরণ করে do মাত্র দুটি stdপাত্রে যে এই কাজের জন্য নিশ্চিত করা হয় হয় vectorএবং array(এবং C-শৈলী অ্যারে যখনই যা ঘটে তখনই!)। dequeএলোমেলো অ্যাক্সেস থাকা সত্ত্বেও এটি সংশ্লেষযোগ্য নয় (বিপজ্জনকভাবে, এটি ছোট অংশগুলিতে মজাদার!), listএমনকি খুব কাছাকাছিও নয়, এবং সহযোগী (অর্ডারযুক্ত এবং অর্ডারযুক্ত) পাত্রেও সমানভাবে স্বতন্ত্র নয়।

তাই তিন কনস্ট্রাকটর আমি কোথায় বাস্তবায়িত std::array, std::vectorএবং C-শৈলী অ্যারে, যা মূলত ঘাঁটি জুড়ে।

বাস্তবায়নও []সহজ এবং এর মধ্যে for()এবং []এটি যা আপনি চান তার বেশিরভাগই arrayতাই না?


এটি কি কেবল অন্য কোথাও টেমপ্লেটটি অফসেট করে না?
GManNickG

নিবন্ধন করুন শিরোনামটি templateবাস্তবায়নের কোনও বিশদ ছাড়াই একটি সংক্ষিপ্ত ফাংশন পায় gets Implফাংশন একটি নয় templateফাংশন, এবং যাতে আপনি সুখে মধ্যে বাস্তবায়ন লুকিয়ে রাখতে পারেন .cppআপনার পছন্দের ফাইল। এটি সত্যিই অপরিশোধিত ধরণের ধরণের ইরেজোর, যেখানে আমি একটি সহজ বর্গক্ষেত্রের মধ্যে কমপেনটিভ পাত্রে পুনরাবৃত্তি করার ক্ষমতাটি বের করি এবং তারপরে পাস করি ... (যখন একটি যুক্তি হিসাবে multArrayImplগ্রহণ করা templateহয়, এটি templateনিজেই নয়)।
ইয়াক্ক - অ্যাডাম নেভ্রামুমন্ট

আমি বুঝতে পারি যে এই অ্যারে ভিউ / অ্যারে প্রক্সি ক্লাসটি কখনও কখনও দরকারী। আমার পরামর্শটি হ'ল কনস্ট্রাক্টরের ধারকটির শুরু / শেষটি পাস করার জন্য যাতে প্রতিটি কন্টেইনারটির জন্য আপনাকে কনস্ট্রাক্টর লিখতে না হয়। এছাড়াও আমি '& * std :: शुरुआत (আরআর)' লিখব না ডিপ্রিফারেন্সিং হিসাবে এবং ঠিকানা নেওয়া এখানে std :: start / std :: শেষ হিসাবে অপ্রয়োজনীয়।
রিকি 65

@ রিকি 65 আপনি যদি পুনরুক্তি ব্যবহার করেন তবে আপনার প্রয়োগটি প্রকাশ করতে হবে। আপনি যদি পয়েন্টার ব্যবহার করেন তবে আপনি তা করবেন না। &*এবং dereferences পুনরুক্তিকারীর (যা একটি পয়েন্টার নাও হতে পারে), তারপর ঠিকানায় একটি পয়েন্টার করে তোলে। সংলগ্ন মেমরি তথ্য জন্য, এখানে পয়েন্টার beginএবং পয়েন্টার এক অতীত- endএছাড়াও রেণ্ডম এক্সেস iterators, এবং তারা একই ধরনের হয় যে একটি টাইপ উপর সংলগ্ন পরিসর T
ইয়াক্ক - অ্যাডাম নেভ্রামামন্ট
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.