স্ট্যান্ডার্ড :: ভেক্টরকে সি ++ এ ফেরানোর কার্যকর উপায়


109

কোনও ফাংশনে স্টাডি :: ভেক্টর ফিরিয়ে দেওয়ার সময় এবং কত বড় অপ্টিমাইজেশন হবে স্ট্যান্ড :: ভেক্টরকে ফ্রি-স্টোরে (হিপের উপরে) স্থাপন করা এবং তার পরিবর্তে একটি পয়েন্টার ফিরিয়ে দেওয়া কতটা অনুলিপি করা হয়:

std::vector *f()
{
  std::vector *result = new std::vector();
  /*
    Insert elements into result
  */
  return result;
} 

এর চেয়ে আরও দক্ষ:

std::vector f()
{
  std::vector result;
  /*
    Insert elements into result
  */
  return result;
} 

?


4
রেফারেন্স দ্বারা ভেক্টরটি পাস করার এবং তারপরে এটি পূরণ করার কীভাবে f?
কিরিল কিরভ

4
আরভিও একটি দুর্দান্ত বুনিয়াদী অপ্টিমাইজেশন যা বেশিরভাগ সংকলক যে কোনও মুহুর্ত করতে সক্ষম হবে।
রিমাস রুসানু

উত্তরগুলি প্রবাহিত হওয়ার সাথে সাথে আপনি এটি C ++ 03 বা C ++ 11 ব্যবহার করছেন কিনা তা স্পষ্ট করতে আপনাকে সহায়তা করতে পারে। দুটি সংস্করণের মধ্যে সেরা অনুশীলনগুলি কিছুটা আলাদা হয়।
ড্রু ডোরম্যান


@ কিরিল কিরভ, আমি কি ফাংশনটির যুক্তি তালিকায় রাখার মাধ্যমে এটি করতে পারি? অকার্যকর চ (এসটিডি :: ভেক্টর এবং ফলাফল)?
মর্টেন

উত্তর:


152

সি ++ 11 এ, এটি পছন্দসই উপায়:

std::vector<X> f();

যে, মান দ্বারা ফিরে।

সি ++ ১১ এর std::vectorসাথে মুভ-সিমানটিকস রয়েছে যার অর্থ আপনার ফাংশনে ঘোষিত স্থানীয় ভেক্টরটি ফিরতি স্থানান্তরিত হবে এবং কিছু ক্ষেত্রে এমনকি মুভটি সংকলক দ্বারা আলাদা করা যেতে পারে।


4
এটি ছাড়াও সরানো হবে std::move?
লিওনিড ভলনিটস্কি

14
@ লিওনিডভোলনিটস্কি: হ্যাঁ যদি এটি স্থানীয় হয় । প্রকৃতপক্ষে, return std::move(v);সরানো-এলিজেন অক্ষম করবে এমনকি এমনকি এটি ন্যায়বিচারের দ্বারা সম্ভব হয়েছিল return v;। সুতরাং পরবর্তী পছন্দ হয়।
নওয়াজ

4
@ জুয়ানচোপাঞ্জা: আমি এমনটা ভাবি না। সি ++ 11 এর আগে, আপনি এর বিরুদ্ধে তর্ক করতে পারেন কারণ ভেক্টর সরানো হবে না; এবং আরভিও একটি সংকলক নির্ভর জিনিস! 80 এবং 90 এর দশকের জিনিসগুলি সম্পর্কে কথা বলুন।
নওয়াজ

4
রিটার্ন মান (মান অনুসারে) সম্পর্কে আমার বোঝার বিষয়টি হ'ল: 'সরানো' পরিবর্তে কলির রিটার্ন মানটি কলারের স্ট্যাকের উপর তৈরি হয়, সুতরাং কলিতে সমস্ত ক্রিয়াকলাপ স্থানে থাকে, আরভিওতে স্থানান্তরিত করার মতো কিছুই নেই is । এটা কি ঠিক?
r0ng

4
@ r0ng: হ্যাঁ, এটা সত্য। সংকলকরা সাধারণত আরভিও বাস্তবায়ন করে।
নওয়াজ

74

আপনার মান দ্বারা ফিরে আসা উচিত।

মান দ্বারা প্রত্যাবর্তনের দক্ষতা উন্নত করতে স্ট্যান্ডার্ডটির একটি নির্দিষ্ট বৈশিষ্ট্য রয়েছে। একে বলা হয় "অনুলিপি এলিজেন", এবং আরও বিশেষত এই ক্ষেত্রে "নামযুক্ত রিটার্ন মান অপ্টিমাইজেশন (এনআরভিও)"।

কম্পাইলার তা বাস্তবায়ন করতে হবে না, কিন্তু তারপর আবার কম্পাইলার না আছে ইনলাইনিং ফাংশন বাস্তবায়ন (অথবা আদৌ কোনো অপ্টিমাইজেশান সঞ্চালন)। সংকলকগণ অনুকূলিত না হলে স্ট্যান্ডার্ড লাইব্রেরিগুলির কার্য সম্পাদন বেশ খারাপ হতে পারে এবং সমস্ত গুরুতর সংকলক ইনলাইনিং এবং এনআরভিও (এবং অন্যান্য অপ্টিমাইজেশান) প্রয়োগ করে।

যখন এনআরভিও প্রয়োগ করা হয়, নিম্নলিখিত কোডটিতে কোনও অনুলিপি থাকবে না:

std::vector<int> f() {
    std::vector<int> result;
    ... populate the vector ...
    return result;
}

std::vector<int> myvec = f();

তবে ব্যবহারকারী এটি করতে চাইতে পারেন:

std::vector<int> myvec;
... some time later ...
myvec = f();

অনুলিপি এলিজেন একটি অনুলিপি এখানে প্রতিরোধ করে না কারণ এটি একটি সূচনা চেয়ে বরং একটি কার্যভার। যাইহোক, আপনি এখনও উচিত মান দ্বারা ফিরে আসা । সি ++ ১১-এ, অ্যাসাইনমেন্টটি আলাদা কিছু দ্বারা অনুকূলিত করা হয়, "মুভ সেমেন্টিকস" called সি ++ তে, উপরের কোডটি একটি অনুলিপি সৃষ্টি করে এবং তাত্ত্বিকভাবে একটি অপ্টিমাইজার এটিকে এড়াতে সক্ষম হতে পারে তবে অনুশীলনে এটি খুব কঠিন। সুতরাং পরিবর্তে myvec = f(), C ++ 03 এ আপনার এটি লেখা উচিত:

std::vector<int> myvec;
... some time later ...
f().swap(myvec);

আরও একটি বিকল্প রয়েছে, যা ব্যবহারকারীকে আরও নমনীয় ইন্টারফেসের প্রস্তাব দেয়:

template <typename OutputIterator> void f(OutputIterator it) {
    ... write elements to the iterator like this ...
    *it++ = 0;
    *it++ = 1;
}

এরপরে আপনি তার উপরে বিদ্যমান ভেক্টর-ভিত্তিক ইন্টারফেসটিকে সমর্থন করতে পারেন:

std::vector<int> f() {
    std::vector<int> result;
    f(std::back_inserter(result));
    return result;
}

এটি আপনার বিদ্যমান কোডের চেয়ে কম দক্ষ হতে পারে, যদি আপনার বিদ্যমান কোডটি reserve()কোনও নির্দিষ্ট পরিমাণের সামনের চেয়ে আরও জটিল উপায়ে ব্যবহার করে। তবে যদি আপনার বিদ্যমান কোডটি মূলত push_backভেক্টরকে বারবার কল করে, তবে এই টেমপ্লেট-ভিত্তিক কোডটি তত ভাল হওয়া উচিত।


4
সত্যই সর্বোত্তম এবং বিশদ উত্তর উপস্থাপন। তবে আপনার অদলবদল () বৈকল্পিক ( সিআর ++ 03 এর জন্য এনআরভিও ব্যতীত ) আপনার কাছে এখনও একটি কপি-কনস্ট্রাক্টর কপি থাকবে চ () এর ভিতরে: ভেরিয়েবলের ফলাফল থেকে কোনও লুকানো অস্থায়ী অবজেক্টে যা শেষ পর্যন্ত মাইভেকে অদলবদল হবে ।
জেনিয়াখ

@ জেনিয়াখ: নিশ্চিত, এটি বাস্তবায়নের একটি মানের বিষয়। স্ট্যান্ডার্ডটির প্রয়োজন হয় না যে সি ++ 03 বাস্তবায়ন এনআরভিও বাস্তবায়িত করবে ঠিক যেমন এটির জন্য ফাংশন ইনলাইনিংয়ের প্রয়োজন হয় না। ফাংশন ইনলাইনিংয়ের পার্থক্যটি হ'ল এনআরভিও যেখানে ইনলাইনিং শব্দার্থবিজ্ঞান বা আপনার প্রোগ্রাম পরিবর্তন করে না। পোর্টেবল কোড অবশ্যই এনআরভিওর সাথে বা ছাড়াই কাজ করবে। একটি নির্দিষ্ট প্রয়োগের জন্য অনুকূলিত কোড (এবং নির্দিষ্ট সংকলক পতাকা) বাস্তবায়নের নিজস্ব ডকুমেন্টেশনে এনআরভিও সম্পর্কিত গ্যারান্টি চাইতে পারে।
স্টিভ জেসোপ

3

এখন আরভিও সম্পর্কে উত্তর পোস্ট করার সময় , আমাকেও ...

যদি আপনি কোনও বস্তুকে মূল্য দিয়ে ফিরিয়ে দেন তবে সংকলকটি প্রায়শই এটি অনুকূল করে তোলে যাতে এটি দুবার নির্মিত না হয়, কারণ এটি অস্থায়ী হিসাবে ফাংশনটিতে এটি নির্মাণ করা এবং তারপরে অনুলিপি করা খুব প্রয়োজন। এটিকে রিটার্ন মান অপ্টিমাইজেশন বলা হয়: তৈরি করা বস্তু অনুলিপি করার পরিবর্তে সরানো হবে।


1

একটি সাধারণ প্রাক সি সি ++ 11 আইডিয়ামটি ভরাট করা বস্তুর একটি রেফারেন্স পাস করা to

তারপরে ভেক্টরের কোনও অনুলিপি নেই।

void f( std::vector & result )
{
  /*
    Insert elements into result
  */
} 

4
এটি C ++ 11-এ আর কোনও প্রতিমা নয়।
নওয়াজ

4
@ নাওয়াজ আমি একমত আমি নিশ্চিত নই যে সি ++ সংক্রান্ত প্রশ্নাবলীর বিষয়ে এখন সুনির্দিষ্ট অনুশীলনটি কী তা কিন্তু বিশেষত সি ++ 11 নয়। আমার সন্দেহ হয় যে আমি একজন শিক্ষার্থীকে সি ++ 11 উত্তর দিতে চাইছি, প্রোডাকশন কোডে কোমর গভীর কাউকে সি ++ 03 উত্তর দেবে। আপনার কি কোন মতামত আছে?
ড্রু ডোরম্যান

7
প্রকৃতপক্ষে, সি ++ 11 প্রকাশের পরে (যা 19 মাস পুরাতন), আমি প্রতিটি প্রশ্নকে সি ++ 11 প্রশ্ন হিসাবে বিবেচনা করি, যদি না এটি স্পষ্টভাবে সি ++ 03 প্রশ্ন হিসাবে বর্ণিত হয়।
নওয়াজ

1

সংকলক যদি নামযুক্ত রিটার্ন মান অপ্টিমাইজেশানকে সমর্থন করে ( http://msdn.microsoft.com/en-us/library/ms364057(v=vs.80).aspx ), আপনি সরাসরি ভেক্টরটি সরবরাহ করতে পারেন যে কোনও নেই:

  1. বিভিন্ন নামযুক্ত জিনিস ফেরত দেওয়া বিভিন্ন পথ
  2. একাধিক রিটার্ন পাথ (যদিও একই নামযুক্ত বস্তুটি সমস্ত পথে ফেরত দেওয়া হয়) EH রাজ্যের সাথে প্রবর্তিত।
  3. প্রত্যাবর্তিত নামযুক্ত বস্তুটি একটি ইনলাইন asm ব্লকে রেফারেন্স করা হবে।

এনআরভিও অনর্থক অনুলিপি নির্মাণকারী এবং ডেস্ট্রাক্টর কলগুলি অনুকূল করে এবং এভাবে সামগ্রিক কর্মক্ষমতা উন্নত করে।

আপনার উদাহরণে কোনও সত্য পার্থক্য হওয়া উচিত।


0
vector<string> getseq(char * db_file)

এবং যদি আপনি এটি মুখ্য () এ মুদ্রণ করতে চান তবে আপনার এটি একটি লুপে করা উচিত।

int main() {
     vector<string> str_vec = getseq(argv[1]);
     for(vector<string>::iterator it = str_vec.begin(); it != str_vec.end(); it++) {
         cout << *it << endl;
     }
}

-2

"মান দ্বারা প্রত্যাবর্তন" যতই সুন্দর হতে পারে, এটি এক ধরণের কোড যা ভুলকে ডেকে আনতে পারে। নিম্নলিখিত প্রোগ্রাম বিবেচনা করুন:

    #include <string>
    #include <vector>
    #include <iostream>
    using namespace std;
    static std::vector<std::string> strings;
    std::vector<std::string> vecFunc(void) { return strings; };
    int main(int argc, char * argv[]){
      // set up the vector of strings to hold however
      // many strings the user provides on the command line
      for(int idx=1; (idx<argc); ++idx){
         strings.push_back(argv[idx]);
      }

      // now, iterate the strings and print them using the vector function
      // as accessor
      for(std::vector<std::string>::interator idx=vecFunc().begin(); (idx!=vecFunc().end()); ++idx){
         cout << "Addr: " << idx->c_str() << std::endl;
         cout << "Val:  " << *idx << std::endl;
      }
    return 0;
    };
  • প্রশ্ন: উপরোক্ত মৃত্যুদণ্ড কার্যকর হলে কী হবে? উঃ একটি কোরেডাম্প।
  • প্রশ্ন: সংকলক ভুলটি কেন ধরেনি? উত্তর: কারণ প্রোগ্রামটি সিনট্যাক্টিক্যালি, যদিও শব্দার্থগতভাবে নয়, সঠিক।
  • প্রশ্ন: আপনি যদি কোনও রেফারেন্স ফেরত দিতে ভেকফুঙ্ক () পরিবর্তন করেন তবে কী হবে? উত্তর: প্রোগ্রামটি সমাপ্তিতে চলে এবং প্রত্যাশিত ফলাফল তৈরি করে।
  • প্রশ্ন: পার্থক্য কী? উত্তর: সংকলক বেনামে অবজেক্ট তৈরি এবং পরিচালনা করতে হবে না। প্রোগ্রামারটি সংকলককে ভাঙা উদাহরণ হিসাবে যেমন দুটি পৃথক বস্তুর পরিবর্তে পুনরাবৃত্তকারী এবং শেষ পয়েন্ট নির্ধারণের জন্য ঠিক একটি অবজেক্ট ব্যবহার করার নির্দেশনা দিয়েছিল।

উপরের ত্রুটিযুক্ত প্রোগ্রামটি কোনও ত্রুটি নির্দেশ করবে এমনকি যদি কেউ জিএনইউ জি ++ রিপোর্টিং বিকল্পগুলি ব্যবহার করে - ওয়াল-ওয়েক্সট্রা-ওয়েফস ++

যদি আপনার অবশ্যই কোনও মান উত্পাদন করতে হয়, তবে নিম্নলিখিতগুলি দুটি বার ভ্যাকফুঙ্ক () কল করার জায়গায় কাজ করবে:

   std::vector<std::string> lclvec(vecFunc());
   for(std::vector<std::string>::iterator idx=lclvec.begin(); (idx!=lclvec.end()); ++idx)...

উপরেরটি লুপটির পুনরাবৃত্তি চলাকালীন কোনও বেনামী অবজেক্টও তৈরি করে না, তবে সম্ভাব্য অনুলিপি অপারেশন প্রয়োজন (যা কিছু পরিস্থিতিতে সম্ভবত অপ্টিমাইজড হতে পারে But তবে রেফারেন্স পদ্ধতিটি গ্যারান্টি দেয় যে কোনও অনুলিপি উত্পাদিত হবে না the সংকলক বিশ্বাস করে আরভিও সম্পাদন করা আপনার পক্ষে সবচেয়ে কার্যকর কোডটি তৈরি করার চেষ্টা করার বিকল্প নয় If আপনি যদি আরভিও করার জন্য সংকলকটির প্রয়োজনটি মোটা করতে পারেন তবে আপনি গেমের চেয়ে এগিয়ে।


4
এটি কোনও ব্যবহারকারী সাধারণভাবে সি ++ এর সাথে পরিচিত না হলে কী ভুল হতে পারে তার একটি উদাহরণ এটি আরও বেশি। নেট বা জাভাস্ক্রিপ্টের মতো অবজেক্ট ভিত্তিক ভাষাগুলির সাথে পরিচিত এমন কেউ সম্ভবত ধরে নেবেন যে স্ট্রিং ভেক্টরটি সর্বদা পয়েন্টার হিসাবে পাস হয় এবং সুতরাং আপনার উদাহরণে সর্বদা একই বস্তুর দিকে নির্দেশ করবে। vecfunc ()। (start।) এবং vecfunc ()। end () অবশ্যই আপনার উদাহরণে মেলে না কারণ তারা স্ট্রিং ভেক্টরের অনুলিপি হওয়া উচিত।
মেদ্রান

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.