কেন আমি কখনও এমপ্লেস_ব্যাকের পরিবর্তে পুশ_ব্যাক ব্যবহার করব?


231

সি ++ 11 ভেক্টরের নতুন ফাংশন রয়েছে emplace_back। ভিন্ন push_back, যা এড়ানোর কপি কম্পাইলার অপ্টিমাইজেশন উপর নির্ভর, emplace_backইন-জায়গা একটি বস্তু তৈরি করতে কন্সট্রাকটর সরাসরি আর্গুমেন্ট পাঠাতে নিখুঁত ফরওয়ার্ডিং ব্যবহার করে। এটি আমার কাছে মনে হয় যা emplace_backকিছু push_backকরতে পারে তা করে তবে কিছু সময় এটি আরও ভাল করে (তবে কখনও খারাপ হয় না)।

আমার কী কারণে ব্যবহার করতে হবে push_back?

উত্তর:


161

আমি গত চার বছর ধরে এই প্রশ্নটি সম্পর্কে বেশ খানিকটা চিন্তা করেছি। আমি এই সিদ্ধান্তে পৌঁছেছি যে push_backবনাম সম্পর্কে সর্বাধিক ব্যাখ্যা emplace_backসম্পূর্ণ চিত্রকে মিস করে।

গত বছর, আমি সি ++ এ সি ++ তে টাইপ ছাড়ের উপর একটি উপস্থাপনা দিয়েছিলাম । আমি 13: push_back49- emplace_backএ বনাম সম্পর্কে কথা বলতে শুরু করি , তবে দরকারী তথ্য রয়েছে যা এর আগে কিছু সহায়ক প্রমাণ সরবরাহ করে।

আসল প্রাথমিক তফাতটি অন্তর্নিহিত বনাম সুস্পষ্ট নির্মাণকারীদের সাথে করতে হবে। কেস আছে যেখানে আমরা একটি একক যুক্তি যে আমরা পাস করতে চান বিবেচনা করুন push_backবা emplace_back

std::vector<T> v;
v.push_back(x);
v.emplace_back(x);

আপনার অনুকূলিতকরণ সংকলকটি এতে হাত পেতে পরে, উত্পন্ন কোডের ক্ষেত্রে এই দুটি বিবৃতিতে কোনও পার্থক্য নেই। Wisdomতিহ্যগত বুদ্ধি হ'ল push_backএকটি অস্থায়ী অবজেক্ট তৈরি করবে, যা পরে স্থানান্তরিত হবে vযেখানে emplace_backযুক্তিটি প্রেরণ করবে এবং কোনও অনুলিপি বা চালনা ছাড়াই সরাসরি জায়গায় এটি নির্মাণ করবে। স্ট্যান্ডার্ড লাইব্রেরিতে লিখিতভাবে কোডের উপর ভিত্তি করে এটি সত্য হতে পারে, তবে এটি ভুল ধারণা তৈরি করে যে অনুকূলিতকরণ সংকলকের কাজটি আপনার লেখা কোডটি উত্পন্ন করা to অপ্টিমাইজ করা সংকলকের কাজটি আসলে আপনি যে কোডটি লিখেছিলেন তা উত্পন্ন করে যদি আপনি প্ল্যাটফর্ম-নির্দিষ্ট অপ্টিমাইজেশনে বিশেষজ্ঞ হন এবং রক্ষণাবেক্ষণের বিষয়ে যত্ন নেন না, কেবল পারফরম্যান্স।

এই দুটি বিবরণের মধ্যে প্রকৃত পার্থক্য হ'ল আরও শক্তিশালী emplace_backসেখানে যে কোনও ধরণের কনস্ট্রাক্টরকে কল করবেন, যেখানে আরও সতর্কতা push_backকেবল সেই কনস্ট্রাক্টরকে কল করবে যা অন্তর্নিহিত। অন্তর্ভুক্ত নির্মাতারা নিরাপদ থাকার কথা। আপনি যদি একটি Uথেকে স্পষ্টতই একটি নির্মাণ করতে পারেন T, আপনি বলছেন যে কোনও ক্ষতি ছাড়াই Uসমস্ত তথ্য ধরে রাখতে পারে T। এটিকে পাস করা মোটামুটি কোনও পরিস্থিতিতে নিরাপদ এবং Tআপনি যদি এটির Uপরিবর্তে তৈরি করেন তবে কেউ আপত্তি করবে না। একটি অন্তর্নিহিত কন্সট্রাকটর একটি ভাল উদাহরণ থেকে রুপান্তরের হয় std::uint32_tথেকে std::uint64_t। একটি অন্তর্নিহিত রূপান্তর একটি খারাপ উদাহরণ doubleথেকে std::uint8_t

আমরা আমাদের প্রোগ্রামিংয়ে সতর্ক হতে চাই। আমরা শক্তিশালী বৈশিষ্ট্যগুলি ব্যবহার করতে চাই না কারণ বৈশিষ্ট্যটি যত বেশি শক্তিশালী, দুর্ঘটনাক্রমে ভুল বা অপ্রত্যাশিতভাবে কিছু করা সহজ। আপনি যদি সুস্পষ্ট নির্মাতাদের কল করতে চান, তবে আপনার পাওয়ারটি দরকার emplace_back। আপনি যদি কেবল অন্তর্নিহিত নির্মাতাদের কল করতে চান তবে এর সুরক্ষার সাথে লেগে থাকুন push_back

একটি উদাহরণ

std::vector<std::unique_ptr<T>> v;
T a;
v.emplace_back(std::addressof(a)); // compiles
v.push_back(std::addressof(a)); // fails to compile

std::unique_ptr<T>এর একটি সুস্পষ্ট নির্মাতা রয়েছে T *। কারণ emplace_backস্বতন্ত্র নির্মাতাদের কল করতে পারে, একটি নন-মালিকানা পয়েন্টারটি ঠিকঠাক সংকলন করে। যাইহোক, যখন vসুযোগের বাইরে চলে যায় , তখন ডেস্ট্রাক্টর deleteসেই পয়েন্টারে কল করার চেষ্টা করবে , যা বরাদ্দ করা হয়নি newকারণ এটি কেবল স্ট্যাক অবজেক্ট। এটি অনির্ধারিত আচরণের দিকে পরিচালিত করে।

এটি কেবল উদ্ভাবিত কোড নয়। এটি একটি সত্যই প্রোডাকশন বাগ ছিল যার মুখোমুখি হয়েছিল। কোডটি ছিল std::vector<T *>, তবে এতে সামগ্রীর মালিকানা ছিল । সি ++ 11 এ মাইগ্রেশনের অংশ হিসাবে, আমি ভেক্টরটির স্মৃতিশক্তির মালিকানা তা চিহ্নিত T *করতে সঠিকভাবে পরিবর্তন করেছি std::unique_ptr<T>। যাইহোক, আমি 2012 সালে আমার বোঝার বন্ধ এই পরিবর্তনগুলি ভিত্তিবিন্দু ছিল, যা সময় আমি ভেবেছিলাম "emplace_back সবকিছু push_back কি করতে পারেন না এবং আরো, তবে কেন আমি কখনো push_back ব্যবহার করেন?", তাই আমিও পরিবর্তিত push_backকরতে emplace_back

যদি আমি পরিবর্তে কোডটি নিরাপদ হিসাবে ব্যবহার করে ছেড়ে চলে যাই, তবে আমি push_backতাত্ক্ষণিকভাবে এই দীর্ঘস্থায়ী বাগটিটি ধরতে পারতাম এবং এটি সি ++ 11 এ আপগ্রেড করার সাফল্য হিসাবে দেখা হত। পরিবর্তে, আমি বাগটি মাস্ক করেছিলাম এবং কয়েক মাস পরে এটি খুঁজে পেলাম না।


এটি আপনার সহায়তা করবে যদি আপনি আপনার উদাহরণে সঠিকভাবে এমপ্লেসগুলি কীভাবে ব্যাখ্যা করেন এবং এটি কেন ভুল।
এডিডি

4
@ এডিডি: আমি std::unique_ptr<T>এটি ব্যাখ্যা করে একটি বিভাগ যুক্ত করেছি: এর একটি সুস্পষ্ট নির্মাতা রয়েছে T *। কারণ emplace_backস্বতন্ত্র নির্মাতাদের কল করতে পারে, একটি নন-মালিকানা পয়েন্টারটি ঠিকঠাক সংকলন করে। যাইহোক, যখন vসুযোগের বাইরে চলে যায় , তখন ডেস্ট্রাক্টর deleteসেই পয়েন্টারে কল করার চেষ্টা করবে , যা বরাদ্দ করা হয়নি newকারণ এটি কেবল স্ট্যাক অবজেক্ট। এটি অনির্ধারিত আচরণের দিকে পরিচালিত করে।
ডেভিড স্টোন

এই পোস্ট করার জন্য ধন্যবাদ। আমি আমার উত্তরটি লেখার সময় এটি সম্পর্কে জানতাম না তবে এখন আমি ইচ্ছা করি আমি নিজেই এটি লিখেছিলাম যখন আমি এটি পরে শিখলাম :) :) আমি সত্যই এমন লোকদের চড় মারতে চাই যাঁরা নতুন বৈশিষ্ট্যগুলিতে স্যুইচ করতে পারেন কেবল তারা খুঁজে পেতে পারে । বন্ধুরা, লোকেরা সি ++ এর আগেও সি ++ ব্যবহার করছিল এবং এটি সম্পর্কে সমস্ত কিছুই সমস্যাযুক্ত ছিল না । আপনি কীভাবে কোনও বৈশিষ্ট্য ব্যবহার করছেন তা যদি আপনি না জানেন তবে এটি ব্যবহার করবেন না । আপনি এই পোস্ট করেছেন তাই খুশি এবং আমি আশা করি এটি আরও বেশি উপায়ে পেয়েছে তাই এটি আমার উপরে। +1
ব্যবহারকারী541686

1
@ ক্যাপ্টেইনজ্যাকসপ্যারো: দেখে মনে হচ্ছে আমি যেখানে বোঝাতে চাইছি সেখানে আমি স্পষ্ট এবং স্পষ্ট বলছি। আপনি কোন অংশটি বিভ্রান্ত করেছেন?
ডেভিড স্টোন

4
@ ক্যাপিটেনজ্যাক্সপ্যারো: explicitকনস্ট্রাক্টর এমন একটি কনস্ট্রাক্টর যার সাথে কীওয়ার্ড explicitপ্রয়োগ করা হয়। একটি "অন্তর্নিহিত" কনস্ট্রাক্টর এমন কোনও কনস্ট্রাক্টর যার কীওয়ার্ডটি নেই। ক্ষেত্রে std::unique_ptr's কন্সট্রাকটর T *, এর প্রয়োগ std::unique_ptrযে কন্সট্রাকটর লিখেছেন, কিন্তু এখানে ইস্যু যে টাইপ নামক ব্যবহারকারী যে emplace_back, যা স্পষ্ট কন্সট্রাকটর বলা হয়। যদি এটি push_backহত তবে সেই কনস্ট্রাক্টরকে কল করার পরিবর্তে এটি অন্তর্নিহিত রূপান্তরের উপর নির্ভর করত, যা কেবলমাত্র অন্তর্নিহিত কনস্ট্রাক্টরকেই কল করতে পারে।
ডেভিড স্টোন

116

push_backসর্বদা অভিন্ন সূচনা ব্যবহারের অনুমতি দেয় যা আমি খুব পছন্দ করি। এই ক্ষেত্রে:

struct aggregate {
    int foo;
    int bar;
};

std::vector<aggregate> v;
v.push_back({ 42, 121 });

অন্যদিকে, v.emplace_back({ 42, 121 });কাজ করবে না।


58
নোট করুন যে এটি কেবল সামগ্রিক সূচনা এবং আরম্ভকারী-তালিকা সূচনাতে প্রযোজ্য। আপনি যদি {}প্রকৃত কনস্ট্রাক্টরকে কল করতে সিনট্যাক্স ব্যবহার করেন , তবে আপনি কেবল এরটি সরিয়ে {}ব্যবহার করতে পারেন emplace_back
নিকল বোলাস

বোবা প্রশ্নের সময়: সুতরাং এমপ্লেস_ব্যাকটি স্ট্রাক্টের ভেক্টরগুলিতে আদৌ ব্যবহার করা যায় না? বা কেবল এই স্টাইলের জন্য আক্ষরিক {42,121} ব্যবহার করছেন না?
ফিল এইচ

1
@ লুকড্যানটন: যেমনটি আমি বলেছি, এটি কেবলমাত্র সামগ্রিক এবং আরম্ভকারী -তালিকার সূচনাতে প্রযোজ্য । আপনি {}প্রকৃত নির্মাণকারীদের কল করতে সিনট্যাক্স ব্যবহার করতে পারেন । আপনি এমন aggregateএকটি কনস্ট্রাক্টর দিতে পারেন যা 2 টি পূর্ণসংখ্যা লাগে এবং {}সিনট্যাক্স ব্যবহার করার সময় এই কনস্ট্রাক্টরকে ডাকা হত । মুল বক্তব্যটি হ'ল আপনি যদি কনস্ট্রাক্টরকে কল করার চেষ্টা করছেন emplace_backতবে এটি ভাল, এবং তাই প্রকারটি অনুলিপি করার প্রয়োজন হয় না।
নিকল বোলাস

11
এটি স্ট্যান্ডার্ডের ত্রুটি হিসাবে দেখা হয়েছিল, এবং এটি সমাধান করা হয়েছে। দেখুন cplusplus.github.io/LWG/lwg-active.html#2089
ডেভিড স্টোন

3
@ ডেভিডস্টোন যদি এটি সমাধান হয়ে যায় তবে এটি এখনও "সক্রিয়" তালিকায় থাকবে না ... না? এটি একটি অসামান্য সমস্যা বলে মনে হচ্ছে। " [2018-08-23 বাতাভিয়া ইস্যু প্রসেসিং] " শীর্ষক সর্বশেষ আপডেটটি বলেছে যে " P0960 (বর্তমানে ফ্লাইটে রয়েছে) এর সমাধান করা উচিত। " এবং আমি এখনও এমন কোড সংকলন করতে পারি না যা emplaceস্পষ্টভাবে বয়লারপ্লেট নির্মাণকারীকে না লিখে সংকলনের চেষ্টা করে can't । এটি এ ক্ষেত্রেও অস্পষ্ট যে এটির কোনও ত্রুটি হিসাবে বিবেচিত হবে এবং ব্যাকপোর্টিংয়ের জন্য এইভাবে যোগ্য হবে, বা সি ++ <20 এর ব্যবহারকারীরা এতক্ষণ সোয়েল থাকবে কিনা।
আন্ডারস্কোর_

80

প্রি-সি ++ 11 সংকলকগুলির সাথে পিছনে সামঞ্জস্য।


21
এটি C ++ এর অভিশাপ বলে মনে হচ্ছে। প্রতিটি নতুন রিলিজের সাথে আমরা প্রচুর শীতল বৈশিষ্ট্য পেয়েছি, তবে প্রচুর সংস্থাগুলি নির্দিষ্ট বৈশিষ্ট্যগুলির সামঞ্জস্যতার জন্য বা নিরুৎসাহিত করার জন্য (যদি প্রত্যাখাত না করে) কিছু পুরানো সংস্করণ ব্যবহার করে আটকে আছে stuck
ড্যান অ্যালবার্ট

6
@ মেহরদাদ: আপনি যখন মহান হতে পারেন তখন পর্যাপ্ত পরিমাণে কেন স্থির করবেন? আমি নিশ্চিত ব্লব প্রোগ্রামিং করতে চাই না , যদিও এটি যথেষ্ট ছিল। এটি উদাহরণস্বরূপ উদাহরণস্বরূপ ক্ষেত্রে না বলছেন না, তবে যে কেউ তাঁর বেশিরভাগ সময় C89- এ সামঞ্জস্যের জন্য প্রোগ্রামিংয়ে ব্যয় করেন, এটি অবশ্যই একটি আসল সমস্যা।
ড্যান অ্যালবার্ট

3
আমি মনে করি না এটি সত্যিই প্রশ্নের উত্তর। আমার কাছে তিনি ব্যবহারের ক্ষেত্রে জিজ্ঞাসা করছেন যেখানে push_backপছন্দনীয়।
মিঃ বয়

3
@ মিঃবয়: আপনি যখন প্রাক-সি ++ 11 সংকলকগুলির সাথে পশ্চাদপটে সামঞ্জস্যপূর্ণ হতে চান তখন এটি পছন্দনীয়। আমার উত্তরে কি তা অস্পষ্ট ছিল?
ব্যবহারকারী541686

6
এই পথ আরো মনোযোগ চেয়ে আমি আশা করে থাকেন অর্জিত হয়েছে তাই এই পড়া তোমাদের সকলের জন্য: emplace_backহয় না একটি "মহান" সংস্করণ push_back। এটি এর সম্ভাব্য বিপজ্জনক সংস্করণ। অন্যান্য উত্তর পড়ুন।
ব্যবহারকারী541686

68

এমপ্লেস_ব্যাকের কয়েকটি গ্রন্থাগার বাস্তবায়ন সি ++ স্ট্যান্ডার্ডে ভিজ্যুয়াল স্টুডিও ২০১২, ২০১৩ এবং ২০১৫ সহ পাঠানো সংস্করণ সহ বর্ণিত আচরণ করে না।

পরিচিত সংকলক বাগগুলি সমন্বয় করার জন্য, std::vector::push_back()পরামিতিগুলি রেফারেন্স পুনরাবৃত্তিকারী বা অন্যান্য বস্তু যা কলের পরে অবৈধ হবে তা ব্যবহার করতে পছন্দ করুন।

std::vector<int> v;
v.emplace_back(123);
v.emplace_back(v[0]); // Produces incorrect results in some compilers

একটি সংকলকটিতে, ভি এর মধ্যে প্রত্যাশিত 123 এবং 123 এর পরিবর্তে 123 এবং 21 এর মান রয়েছে This এটি দ্বিতীয় emplace_backকারণের ফলে পুনরায় আকার দেবে যার ফলে বিন্দুটি v[0]অবৈধ হয়ে যায় to

উপরের কোডটির একটি কার্যকরী বাস্তবায়ন নীচের push_back()পরিবর্তে ব্যবহার করবে emplace_back():

std::vector<int> v;
v.emplace_back(123);
v.push_back(v[0]);

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


অপেক্ষা করুন। এটি একটি ত্রুটি বলে মনে হচ্ছে। push_backএই ক্ষেত্রে কীভাবে আলাদা হতে পারে?
বলকি

12
এমপ্লেস_ব্যাক () এ কলটি স্থানে নির্মাণের জন্য নিখুঁত ফরোয়ার্ডিং ব্যবহার করে এবং যেমন ভেক্টরকে পুনরায় আকার না দেওয়ার (যেমন পয়েন্ট v [0] অবৈধ) অবধি অবধি মূল্যায়ন করা হয় না is পুশ_ব্যাক নতুন উপাদানটি তৈরি করে এবং প্রয়োজনীয় উপাদানটি অনুলিপি করে / চালায় এবং v [0] কোনও পুনঃব্যবস্থাপনের পূর্বে মূল্যায়ন করা হয়।
মার্ক 16

1
@ মার্ক: এটি এমন মানদণ্ডের দ্বারা গ্যারান্টিযুক্ত যা এমপ্লেস_ব্যাক এমনকি সীমার অভ্যন্তরের উপাদানগুলির জন্যও কাজ করে।
ডেভিড স্টোন

1
@ ডেভিডস্টোন: অনুগ্রহ করে এমন কোনও রেফারেন্স সরবরাহ করতে পারেন যেখানে স্ট্যান্ডার্ডে এই আচরণের নিশ্চয়তা দেওয়া হয়েছে? যে কোনও উপায়ে, ভিজ্যুয়াল স্টুডিও 2012 এবং 2015 ভুল আচরণ প্রদর্শন করে।
মার্চ

4
@ ক্যামেনিনো: এমপ্লেস_ব্যাক অপ্রয়োজনীয় অনুলিপি হ্রাস করার জন্য এর প্যারামিটারের মূল্যায়ন বিলম্বের জন্য বিদ্যমান। আচরণটি হয় পূর্বনির্ধারিত বা একটি সংকলক বাগ (মানক বিশ্লেষণের মুলতুবি)। আমি সম্প্রতি ভিজ্যুয়াল স্টুডিও 2015 এর বিপরীতে একই পরীক্ষা চালিয়েছি এবং রিলিজ এক্স 64 এর অধীনে 123,3 পেয়েছি, রিলিজ উইন 32 এর অধীনে 123,40 এবং ডিবাগ x64 এর অধীনে -572662307 এবং ডিবাগ উইন 32 পেয়েছি।
মার্চ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.