ফাংশন থেকে অনন্য_পিটার ফিরে আসা


367

unique_ptr<T>অনুলিপি নির্মাণের অনুমতি দেয় না, পরিবর্তে এটি পদক্ষেপ পদার্থকে সমর্থন করে। তবুও, আমি unique_ptr<T>একটি ফাংশন থেকে একটি ফেরত দিতে এবং ফেরত মানটি একটি ভেরিয়েবলের জন্য বরাদ্দ করতে পারি।

#include <iostream>
#include <memory>

using namespace std;

unique_ptr<int> foo()
{
  unique_ptr<int> p( new int(10) );

  return p;                   // 1
  //return move( p );         // 2
}

int main()
{
  unique_ptr<int> p = foo();

  cout << *p << endl;
  return 0;
}

উপরের কোডটি সংকলন করে এবং উদ্দেশ্য হিসাবে কাজ করে। সুতরাং এটি কীভাবে লাইনটি 1অনুলিপি নির্মাণকারীকে অনুরোধ করে না এবং সংকলক ত্রুটির ফলাফল করে? এর 2পরিবর্তে যদি আমাকে লাইন ব্যবহার করতে হয় তবে এটি বোধগম্য হবে (লাইনটি 2পাশাপাশি কাজ করে তবে আমাদের এটি করার প্রয়োজন হয় না)।

আমি জানি সি ++ 0 এক্স এই ব্যতিক্রমটিকে মঞ্জুরি দেয় unique_ptrকারণ যেহেতু রিটার্ন মান একটি অস্থায়ী বস্তু যা ফাংশনটি বেরিয়ে আসার সাথে সাথেই ধ্বংস হয়ে যায়, সুতরাং প্রত্যাবর্তকের পয়েন্টারের স্বতন্ত্রতার গ্যারান্টি দেয়। এটি কীভাবে বাস্তবায়িত হয় সে সম্পর্কে আমি কৌতূহল বোধ করি, এটি কি সংকলকটিতে বিশেষ বিশেষভাবে তৈরি করা হয়েছে বা ভাষা ব্যবহারের ক্ষেত্রে অন্য কোনও ধারা রয়েছে যা এটি ব্যবহার করে?


হাইপোথিটিক্যালি, আপনি যদি কারখানার পদ্ধতি প্রয়োগ করেন , আপনি কি কারখানার আউটপুট ফেরত দিতে 1 বা 2 পছন্দ করবেন? আমি অনুমান করি যে এটি 1 এর সর্বাধিক সাধারণ ব্যবহার হবে কারণ সঠিক কারখানার সাথে আপনি প্রকৃতপক্ষে নির্ধারিত জিনিসের মালিকানা কলারের কাছে যেতে চান।
Xharlie

7
@ ক্ষারলি? তারা উভয় মালিকানা পাস unique_ptr। পুরো প্রশ্নটি প্রায় 1 এবং 2 একই জিনিস অর্জনের দুটি ভিন্ন উপায়।
প্রিটোরিয়ান

এই ক্ষেত্রে, আরভিও সি ++ 0 এক্স-তেও স্থান নেয়, অনন্য_পট্র অবজেক্টের ধ্বংস একবার হয়ে যাবে যা mainফাংশন থেকে বেরিয়ে যাওয়ার পরে সঞ্চালিত হয় , তবে যখন fooপ্রস্থান হয় না।
এমপ্যাভড

উত্তর:


218

ভাষার স্পেসিফিকেশনের আরও কিছু ধারা রয়েছে যা এটি কাজে লাগায়?

হ্যাঁ, 12.8 §34 এবং §35 দেখুন:

যখন নির্দিষ্ট মানদণ্ডগুলি মেটানো হয়, কোনও প্রয়োগকে কোনও শ্রেণীর অবজেক্টের অনুলিপি / সরানো নির্মাণ বাদ দেওয়ার অনুমতি দেওয়া হয় [...] অনুলিপি / সরানো ক্রিয়াকলাপের এই এলিজেন্সকে, কপির এলিজেন বলে , অনুমোদিত [...] ক্লাস রিটার্ন টাইপের একটি ফাংশন, যখন এক্সপ্রেশনটি একই সিভি-অযোগ্যতাযুক্ত টাইপ সহ ফাংশন রিটার্ন টাইপের মতো একটি অ-উদ্বায়ী স্বয়ংক্রিয় বস্তুর নাম হয় [...]

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


আরও একটি পয়েন্ট যোগ করতে চেয়েছিলেন যে মান দ্বারা প্রত্যাবর্তনটি এখানে ডিফল্ট পছন্দ হওয়া উচিত কারণ সবচেয়ে খারাপ ক্ষেত্রে রিটার্নের স্টেটমেন্টের একটি নামকৃত মান, যেমন সি ++ 11, সি ++ 14 এবং সি ++ 17 তে এলেন্স ছাড়াই চিকিত্সা করা হয় মূল্য হিসাবে সুতরাং উদাহরণস্বরূপ নীচের ফাংশন -fno-elide-constructorsপতাকা সঙ্গে সংকলন

std::unique_ptr<int> get_unique() {
  auto ptr = std::unique_ptr<int>{new int{2}}; // <- 1
  return ptr; // <- 2, moved into the to be returned unique_ptr
}

...

auto int_uptr = get_unique(); // <- 3

সংকলনের উপরে পতাকা সেট করার সাথে এই ফাংশনে দুটি চলন (1 এবং 2) হয় এবং তারপরে একটি পদক্ষেপ পরে যায় (3)।


@ জুয়ানচোপাঞ্জা আপনি কি মূলত বলতে চাইছেন যে foo()এটি ফাংশনটির মধ্যে রিটার্ন ভ্যালুর মতোই (এমনকি যদি এটি কোনও কিছুর কাছে অর্পণ করা হয়নি) ধ্বংস হতে চলেছে, এবং সুতরাং এটি বোঝা যায় যে সি ++ কাজ করার সময় একটি মুভ কনস্ট্রাক্টর ব্যবহার করে unique_ptr<int> p = foo();?
7cows

1
এই উত্তরটি বলছে একটি বাস্তবায়নের জন্য কিছু করার অনুমতি দেওয়া আছে ... এটি এটি বলে না যে এটি অবশ্যই করা উচিত, সুতরাং যদি এটি কেবলমাত্র প্রাসঙ্গিক বিভাগই ছিল তবে বোঝা যে এই আচরণের উপর নির্ভর করা বহনযোগ্য নয়। তবে আমি ঠিক মনে করি না। নিকোলা স্মিলজানিক এবং বার্তোসজ মাইলুস্কির উত্তরে বর্ণিত মতামতটি সঠিক উত্তরটির সাথে মুভ কনস্ট্রাক্টরের সাথে আরও জড়িত থাকার কথা ভাবতে চাইছি।
ডন হ্যাচ

6
@ ডনহ্যাচ এটি বলছে যে এই ক্ষেত্রে কপি / মুভিং এলিজেন্স সম্পাদন করার "অনুমতি দেওয়া" হয়েছে, তবে আমরা এখানে অনুলিপি নিয়ে কথা বলছি না। এটি এখানে প্রযোজ্য দ্বিতীয় উদ্ধৃত অনুচ্ছেদ, যা কপির এলিজেন্স নিয়মের পিগি-সমর্থন করে, তবে নিজেই অনুলিপি করে না। দ্বিতীয় অনুচ্ছেদে কোনও অনিশ্চয়তা নেই - এটি সম্পূর্ণ পোর্টেবল।
জোসেফ ম্যানসফিল্ড

@ জুয়ানচোপাঞ্জা আমি বুঝতে পারি যে এটি এখন 2 বছর পরে, কিন্তু আপনি কি এখনও মনে করেন যে এটি ভুল? আমি আগের মন্তব্যে যেমন উল্লেখ করেছি, এটি অনুলিপি সংক্রান্ত নয়। এটি ঠিক তাই ঘটে থাকে যে ক্ষেত্রে কপি এলিজেন প্রয়োগ হতে পারে (এটি প্রয়োগ না করতে std::unique_ptrপারলেও), প্রথমে অবজেক্টকে যথাযথ হিসাবে বিবেচনা করার জন্য একটি বিশেষ বিধি রয়েছে। আমি মনে করি এটি নিকোলা যে উত্তর দিয়েছে তার সাথে পুরোপুরি একমত।
জোসেফ ম্যানসফিল্ড

1
সুতরাং কেন আমি এখনও আমার মুভ-টাইপ টাইপের (মুছে ফেলা কপিরাইটার) মুছে ফেলার জন্য "মুছে ফাংশনটি উল্লেখ করার চেষ্টা করা" ত্রুটিটি পেয়েছি কেন ঠিক এই উদাহরণটির মতো ঠিক একইভাবে ফেরত দেওয়ার সময়?
ড্রামএম

104

এটি std::unique_ptrকোনওভাবেই সুনির্দিষ্ট নয় , তবে চলমান যে কোনও শ্রেণির ক্ষেত্রে এটি প্রযোজ্য। আপনি যে মূল্য দিয়ে ফিরে আসছেন সেহেতু এটি ভাষার নিয়মগুলির দ্বারা গ্যারান্টিযুক্ত। সংকলকটি অনুলিপিগুলি সহীকরণের চেষ্টা করে, অনুলিপিগুলি অপসারণ করতে না পারলে একটি মুভ কনস্ট্রাক্টরকে অনুরোধ করে, সরানো না পারলে একটি অনুলিপি নির্মাণকারীকে কল করে এবং এটি অনুলিপি না করতে পারলে সংকলন করতে ব্যর্থ হয়।

আপনার যদি এমন কোনও ফাংশন থাকে যা std::unique_ptrআর্গুমেন্ট হিসাবে গ্রহণ করে তবে আপনি তাতে পিটি দিতে পারবেন না। আপনাকে স্পষ্টভাবে মুভ কনস্ট্রাক্টরকে অনুরোধ করতে হবে, তবে এই ক্ষেত্রে আপনার কল করার পরে ভেরিয়েবল পি ব্যবহার করা উচিত নয় bar()

void bar(std::unique_ptr<int> p)
{
    // ...
}

int main()
{
    unique_ptr<int> p = foo();
    bar(p); // error, can't implicitly invoke move constructor on lvalue
    bar(std::move(p)); // OK but don't use p afterwards
    return 0;
}

3
@ ফ্রেড - ভাল, আসলে না। যদিও pএটি সাময়িক নয়, এর ফলাফল foo(), যা ফিরিয়ে দেওয়া হচ্ছে, তা হ'ল; এইভাবে এটি একটি মূল্যায়ন এবং সরানো যেতে পারে, যা কার্যভারটি mainসম্ভব করে তোলে । আমি বলব আপনি যদি ভুল হয়ে থাকেন তবে নিকোলা তখন মনে হয় যে এই বিধিটি pনিজেই প্রয়োগ করে যা ভুল in
এডওয়ার্ড স্ট্রেঞ্জ

ঠিক আমি কী বলতে চেয়েছিলাম তবে শব্দগুলি খুঁজে পেল না। আমি উত্তরের সেই অংশটি সরিয়ে দিয়েছি কারণ এটি খুব পরিষ্কার ছিল না।
নিকোলা স্মিলজানিć

আমার একটি প্রশ্ন আছে: মূল প্রশ্নে লাইন 1এবং লাইনের মধ্যে কোনও উল্লেখযোগ্য পার্থক্য রয়েছে 2কি? আমার দৃষ্টিতে এটি একইরূপে নির্মাণের pসময় main, এটি কেবলমাত্র ফেরতের ধরণের সম্পর্কেই যত্নশীল foo, তাই না?
হংকক্সু চেন

1
@ হংক্সুচেন সেই উদাহরণে একেবারে কোনও পার্থক্য নেই, গৃহীত উত্তরের মান থেকে উদ্ধৃতিটি দেখুন।
নিকোলা স্মিলজানিć

প্রকৃতপক্ষে, আপনি পি ব্যবহার করতে পারেন, যতক্ষণ আপনি এটি নির্ধারণ করেন। ততক্ষণ আপনি বিষয়বস্তু উল্লেখ করার চেষ্টা করতে পারবেন না।
অ্যালান

38

অনন্য_পিটারের theতিহ্যবাহী অনুলিপি নির্মাণকারী নেই। পরিবর্তে এটিতে "মুভ কনস্ট্রাক্টর" রয়েছে যা মূলসূত্র উল্লেখগুলি ব্যবহার করে:

unique_ptr::unique_ptr(unique_ptr && src);

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

যাইহোক, এটি সঠিকভাবে কাজ করবে:

bar(unique_ptr<int>(new int(44));

এখানে অস্থায়ী অনন্য_পিটার একটি মূল্যায়ন।


8
আমি মনে করি যে বিন্দুটি আরও বেশি, কেন p- "স্পষ্টতই" একটি ললভ - এর সংজ্ঞা অনুসারে রিটার্ন স্টেটমেন্টকে একটি মূল্য হিসাবে বিবেচনা করা যেতে পারে । আমি মনে করি না যে ফাংশনটির রিটার্ন মান নিজেই "সরানো" যেতে পারে তাতে কোনও সমস্যা নেই। return p;foo
সিবি বেইলি

Std :: মুছে ফাংশন থেকে ফেরত মানটি মোড়ানো মানে কি এটি দুটিবার সরানো হবে?

3
@ রডরিগোসালাজার স্টাডি :: পদক্ষেপটি একটি মূল্যবান রেফারেন্স (&) থেকে কোনও মূল্যের রেফারেন্স (&&) এর কাছে অভিনব কাস্ট। স্ট্যান্ডার্ডের বহিঃপ্রকাশের ব্যবহার :: কোনও মূলসূত্রের রেফারেন্সে স্থানান্তর করা কেবল নূপুর হবে
টিমোচ

13

আমি মনে করি স্কট মেয়ার্সের কার্যকর আধুনিক সি ++ এর আইটেম 25 এ এটি পুরোপুরি ব্যাখ্যা করা হয়েছে । এখানে একটি অংশ:

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

এখানে, আরভিও রিটার্ন মান অপ্টিমাইজেশন বোঝায় , এবং যদি আরভিওর শর্ত পূরণ হয় তবে ফাংশনটির ভিতরে ঘোষিত স্থানীয় বস্তুটি ফিরে আসার অর্থ আপনি আরভিও করার প্রত্যাশা করবেন , যা উল্লেখ করে তাঁর বইয়ের আইটেম 25 এও সুন্দরভাবে ব্যাখ্যা করা হয়েছে is স্ট্যান্ডার্ড (এখানে স্থানীয় অবজেক্টে রিটার্ন স্টেটমেন্ট দ্বারা তৈরি অস্থায়ী বস্তু অন্তর্ভুক্ত রয়েছে)। উদ্ধৃতি থেকে সবচেয়ে বড় দূরে হয় কপির এলিজেন হয় বা std::moveস্থানীয় অবজেক্টগুলি প্রত্যাবর্তনের ক্ষেত্রে স্পষ্টভাবে প্রয়োগ করা হয় । স্কট 25 আইটেমটিতে উল্লেখ করেছে std::moveযা সংকলক অনুলিপিটি প্রচ্ছদ না করা এবং প্রোগ্রামার স্পষ্টভাবে এটি করা উচিত নয় যখন প্রয়োগ করা হয়।

আপনার ক্ষেত্রে, কোডটি স্পষ্টতই আরভিওর জন্য প্রার্থী হিসাবে এটি স্থানীয় অবজেক্টকে রিটার্ন দেয় pএবং এর প্রকারটি pরিটার্নের ধরণের সমান হয়, যার ফলে অনুলিপি এলিজেন হয়। এবং যদি সংকলক কোনও কারণেই অনুলিপিটি পছন্দ করে না, std::moveতবে লাইনে লাথি মারবে 1


5

একটি জিনিস যা আমি অন্যান্য উত্তরে দেখিনিঅন্য উত্তরগুলি পরিষ্কার করার জন্য যে কোনও ফাংশনের মধ্যেই তৈরি করা হয়েছে std :: অনন্য_পিটারের মধ্যে পার্থক্য রয়েছে এবং সেই ফাংশনটিতে একটি দেওয়া হয়েছে।

উদাহরণটি এর মতো হতে পারে:

class Test
{int i;};
std::unique_ptr<Test> foo1()
{
    std::unique_ptr<Test> res(new Test);
    return res;
}
std::unique_ptr<Test> foo2(std::unique_ptr<Test>&& t)
{
    // return t;  // this will produce an error!
    return std::move(t);
}

//...
auto test1=foo1();
auto test2=foo2(std::unique_ptr<Test>(new Test));

ফ্রেডওভারফ্লো দ্বারা এটি উত্তরে উল্লেখ করা হয়েছে - স্পষ্টভাবে " স্বয়ংক্রিয় বস্তু" হাইলাইট হয়েছে । একটি রেফারেন্স (কোনও মূল্যের রেফারেন্স সহ) কোনও স্বয়ংক্রিয় বস্তু নয়।
টবি স্পিড

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