সি ++ মানচিত্রে বনাম এমপ্লেস বনাম অপারেটর ]োকান


188

আমি প্রথমবারের জন্য মানচিত্রগুলি ব্যবহার করছি এবং আমি বুঝতে পেরেছি যে একটি উপাদান sertোকানোর অনেকগুলি উপায় রয়েছে। আপনি ব্যবহার করতে পারেন emplace(), operator[]বা insert(), প্লাস ব্যবহার করার মত রূপ value_typeবা make_pair। যদিও এগুলির সমস্ত সম্পর্কে প্রচুর তথ্য এবং নির্দিষ্ট কেস সম্পর্কিত প্রশ্ন রয়েছে তবে আমি এখনও বড় চিত্রটি বুঝতে পারি না। সুতরাং, আমার দুটি প্রশ্ন হ'ল:

  1. অন্যদের থেকে তাদের প্রত্যেকের কী লাভ?

  2. স্ট্যান্ডার্ডে এমপ্লেস যুক্ত করার কোনও দরকার ছিল কি? এমন কিছু আছে যা আগে ছাড়া সম্ভব ছিল না?


1
এমপ্লেসমেন্ট শব্দার্থক স্পষ্ট রূপান্তর এবং সরাসরি সূচনা করার অনুমতি দেয়।
কেরেক এসবি

3
এখন operator[]ভিত্তিক হয় try_emplace। এটি insert_or_assignপাশাপাশি উল্লেখযোগ্য হতে পারে ।
ফ্র্যাঙ্কএইচবি

@ ফ্র্যাঙ্কএইচবি আপনি যদি (বা অন্য কেউ) একটি আপ-টু-ডেট উত্তর যুক্ত করেন তবে আমি গৃহীত উত্তরটি পরিবর্তন করতে পারি।
জার্মান ক্যাপুয়ানো

উত্তর:


226

কোনও মানচিত্রের বিশেষ ক্ষেত্রে পুরাতন বিকল্পগুলি দুটি ছিল: operator[]এবং insert(এর স্বাদ বিভিন্ন insert)। সুতরাং আমি তাদের ব্যাখ্যা শুরু করব।

operator[]একটি হল খোঁজ-অর-অ্যাড অপারেটর। এটি মানচিত্রের অভ্যন্তরে প্রদত্ত কী সহ একটি উপাদান সন্ধান করার চেষ্টা করবে এবং এটি উপস্থিত থাকলে এটি সঞ্চিত মানটির একটি রেফারেন্স ফিরিয়ে দেবে। যদি এটি না হয়, এটি ডিফল্ট সূচনা সহ জায়গায় sertedোকানো একটি নতুন উপাদান তৈরি করবে এবং এটিতে একটি রেফারেন্স ফিরিয়ে দেবে।

insertফাংশন (একক উপাদান গন্ধ) একটি লাগে value_type( std::pair<const Key,Value>), এটা কী (ব্যবহার firstসদস্য) এবং এটি সন্নিবেশ করার চেষ্টা করে। কারণ std::mapকোনও বিদ্যমান উপাদান থাকলে এটি সদৃশ হওয়ার অনুমতি দেয় না এটি কোনও কিছুই প্রবেশ করবে না।

উভয়ের মধ্যে প্রথম পার্থক্যটি হ'ল operator[]ডিফল্ট আরম্ভীকৃত মানটি তৈরি করতে সক্ষম হওয়া দরকার এবং এটি মান ধরণের ক্ষেত্রে অব্যর্থ যেগুলি ডিফল্ট আরম্ভ করা যায় না। উভয়ের মধ্যে দ্বিতীয় পার্থক্য হ'ল যখন প্রদত্ত কীটির সাথে ইতিমধ্যে কোনও উপাদান উপস্থিত থাকে তখন কী হয়। insertফাংশন মানচিত্রের রাষ্ট্র সংশোধন করা হবে না, কিন্তু এর পরিবর্তে উপাদান (এবং একটি পুনরুক্তিকারীর আসতে falseনির্দেশ করে এটা সন্নিবেশিত করা হয় নি)।

// assume m is std::map<int,int> already has an element with key 5 and value 0
m[5] = 10;                      // postcondition: m[5] == 10
m.insert(std::make_pair(5,15)); // m[5] is still 10

ক্ষেত্রে insertযুক্তি হল একটি অবজেক্ট value_type, যা বিভিন্ন উপায়ে তৈরি করা যায়। আপনি এটি যথাযথ প্রকারের সাহায্যে সরাসরি নির্মাণ করতে পারেন বা যে কোনও বস্তুটি তৈরি করা value_typeযেতে পারে যা পাস হতে পারে, যেখানে std::make_pairএটি খেলতে আসে, কারণ এটি std::pairঅবজেক্টগুলির সহজতর নির্মাণের অনুমতি দেয় , যদিও এটি সম্ভবত আপনি চান না ...

নিম্নলিখিত কলগুলির নেট এফেক্ট একই রকম :

K t; V u;
std::map<K,V> m;           // std::map<K,V>::value_type is std::pair<const K,V>

m.insert( std::pair<const K,V>(t,u) );      // 1
m.insert( std::map<K,V>::value_type(t,u) ); // 2
m.insert( std::make_pair(t,u) );            // 3

তবে এগুলি আসলে একই নয় ... [1] এবং [2] আসলে সমতুল্য। উভয় ক্ষেত্রেই কোড একই ধরণের ( std::pair<const K,V>) অস্থায়ী বস্তু তৈরি করে এবং এটি insertফাংশনে প্রেরণ করে । insertফাংশন বাইনারি অনুসন্ধান বৃক্ষ যথাযথ নোড তৈরি করবে এবং তারপর কপি value_typeনোডের যুক্তি থেকে অংশ। ব্যবহারের সুবিধাটি value_typeহ'ল, ভাল, value_typeসর্বদা মেলে value_type , আপনি std::pairআর্গুমেন্টের ধরণটি ভুল টাইপ করতে পারবেন না !

পার্থক্যটি [3] এ রয়েছে। ফাংশনটি std::make_pairএকটি টেম্পলেট ফাংশন যা একটি তৈরি করবে std::pair। স্বাক্ষরটি হ'ল:

template <typename T, typename U>
std::pair<T,U> make_pair(T const & t, U const & u );

আমি ইচ্ছাকৃতভাবে টেমপ্লেট আর্গুমেন্ট সরবরাহ করিনি std::make_pair, কারণ এটি সাধারণ ব্যবহার। এবং প্রভাবটি হ'ল এই ক্ষেত্রে, টেমপ্লেট আর্গুমেন্টগুলি কল থেকে অনুমিত করা হয় T==K,U==V, সুতরাং কলটি কলটি std::make_pairএকটি std::pair<K,V>(অনুপস্থিত নোটটি const) ফেরত দেবে । স্বাক্ষরটির প্রয়োজন value_typeযা কাছাকাছি হলেও কল থেকে কলটি ফেরতের মূল্যের মতো নয় std::make_pair। কারণ এটি যথেষ্ট কাছাকাছি এটি সঠিক ধরণের একটি অস্থায়ী তৈরি করবে এবং এটি অনুলিপি করবে। এগুলি নোডে অনুলিপি করা হবে এবং মোট দুটি অনুলিপি তৈরি করবে।

এটি টেমপ্লেট আর্গুমেন্ট সরবরাহ করে স্থির করা যেতে পারে:

m.insert( std::make_pair<const K,V>(t,u) );  // 4

তবে এটি এখনও ত্রুটিযুক্ত একইরকম প্রবণতা যা স্পষ্টভাবে টাইপ টাইপের ক্ষেত্রে [1]।

এই অবধি, আমাদের কল করার বিভিন্ন উপায় রয়েছে যার insertজন্য value_typeবাহ্যিকভাবে তৈরি এবং ধারকটির মধ্যে সেই বস্তুর অনুলিপি প্রয়োজন require বিকল্পটি যদি আপনি operator[]টাইপটি ডিফল্ট গঠনযোগ্য এবং নির্ধারিত (ইচ্ছাকৃতভাবে কেবলমাত্র এতে ফোকাস করা হয় m[k]=v) ব্যবহার করতে পারেন তবে এটির জন্য একটি অবজেক্টের ডিফল্ট সূচনা এবং সেই বস্তুর মধ্যে মানটির অনুলিপি প্রয়োজন

সি ++ ১১-এ, বৈকল্পিক টেমপ্লেট এবং নিখুঁত ফরোয়ার্ড সহ একটি পাত্রে empর্ধ্বকরণের ( উপাদান তৈরির) মাধ্যমে উপাদান যুক্ত করার একটি নতুন উপায় রয়েছে । emplaceবিভিন্ন পাত্রে ফাংশন মূলত একই জিনিস করে: একটি হবার বদলে উৎস যা থেকে কপি ধারক মধ্যে, ফাংশন পরামিতি যে বস্তুর কন্টেইনারে সঞ্চিত কন্সট্রাকটর ফরোয়ার্ড করা হবে লাগে।

m.emplace(t,u);               // 5

[]] -তে, এটি তৈরি করা std::pair<const K, V>হয় না এবং এতে প্রেরণ করা হয় না emplace, বরং তথ্য tuঅবজেক্টের উল্লেখগুলি ডেটা স্ট্রাকচারের অভ্যন্তরে সাববোজেক্টের emplaceকনস্ট্রাক্টরের কাছে পাঠানো হয় value_type। এই ক্ষেত্রে কোন কপি std::pair<const K,V>এ সব কাজ হয়ে গেলে, এর সুবিধা যা emplaceসি ++ 03 বিকল্প করে। insertএটির ক্ষেত্রে মানচিত্রে মানটি ওভাররাইড করবে না।


একটি আকর্ষণীয় প্রশ্ন যা আমি ভেবে দেখিনি তা হ'ল কীভাবে emplaceআসলে মানচিত্রের জন্য বাস্তবায়ন করা যায় এবং এটি সাধারণ ক্ষেত্রে সাধারণ সমস্যা নয়।


5
এটি উত্তরে ইঙ্গিতযুক্ত, তবে মানচিত্র [] = ভল যদি বিদ্যমান থাকে তবে পূর্বের মানটি ওভাররাইট করে।
dk123

আমার মতে আরও আকর্ষণীয় প্রশ্ন হ'ল এটি খুব সামান্য উদ্দেশ্য করে। কারণ আপনি এই জুটির অনুলিপি সংরক্ষণ করেছেন, যা ভাল কারণ কোনও জোড় অনুলিপি মানেই কোনও mapped_typeআইট্যানস কপি নেই। আমরা যা চাই তা হ'ল mapped_typeজোড়ের মধ্যে তৈরির কাজকে সহজ করে তোলা এবং মানচিত্রে জোড় নির্মাণকে সহজ করে তুলুন। সুতরাং, std::pair::emplaceফাংশন এবং এর ফরোয়ার্ডিং সমর্থন map::emplaceউভয়ই অনুপস্থিত। বর্তমান রূপে, আপনাকে এখনও জোড়া কন্সট্রাক্টরকে একটি নির্মিত ম্যাপড টাইপ দিতে হবে যা একবার এটি অনুলিপি করবে। এটি দ্বিগুণ চেয়ে ভাল, কিন্তু এখনও ভাল না।
v.oddou

আসলে আমি সেই মন্তব্যে সংশোধন করেছি, সি ++ 11 তে একটি টেম্পলেট জুটি নির্মাণকারী রয়েছে যা 1 টি আর্গুমেন্ট নির্মাণের ক্ষেত্রে এমপ্লেসের চেয়ে ঠিক একই উদ্দেশ্যে কাজ করে। এবং কিছু অদ্ভুত টুকরো টুকরো টুকরো টুকরো টানা নির্মাণ করে, যেমন তারা আর্গুমেন্ট ফরোয়ার্ডে টিপলগুলি ব্যবহার করে, যাতে আমাদের এখনও নিখুঁত ফরোয়ার্ডিং থাকতে পারে বলে মনে হয়।
v.oddou

দেখে মনে হচ্ছে এ unordered_map এবং মানচিত্র সন্নিবেশ একটি কর্মক্ষমতা বাগ থাকে: লিংক
Deqing

1
এটিকে তথ্যের সাথে insert_or_assignএবং try_emplace(সি ++ 17 উভয় থেকে) আপডেট করে খুব সুন্দর হতে পারে , যা বিদ্যমান পদ্ধতিগুলি থেকে কার্যকারিতার কিছু ফাঁক পূরণ করতে সহায়তা করে।
শ্যাডোর্যাঞ্জার

14

এম্পলেস: আপনার ইতিমধ্যে তৈরি করা প্রকৃত অবজেক্টগুলি ব্যবহার করতে যথাযথ রেফারেন্সের সুবিধা নেয়। এর অর্থ হ'ল কোনও অনুলিপি বা মুভ কনস্ট্রাক্টর বলা হয় না, LARGE অবজেক্টের জন্য ভাল! ও (লগ (এন)) সময়।

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

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

Make_pair: জুটি তৈরির চেয়ে কিছু বেশি করে।

স্ট্যান্ডার্ডে এমপ্লেস যুক্ত করার জন্য কোনও "প্রয়োজন" ছিল না। সি ++ 11-এ আমি বিশ্বাস করি যে && & quot; প্রকারের রেফারেন্স যুক্ত হয়েছিল। এটি পদক্ষেপ পদার্থবিজ্ঞানের প্রয়োজনীয়তা সরিয়ে ফেলে এবং কিছু নির্দিষ্ট ধরণের মেমরি পরিচালনার অপ্টিমাইজেশনের অনুমতি দেয়। বিশেষত, মূল্যের রেফারেন্স। ওভারলোডেড সন্নিবেশ (মান_প্রকার ও&) অপারেটর ইন_প্লেস শব্দার্থকতার সুবিধা নেয় না, এবং তাই খুব কম দক্ষ। যদিও এটি মূল্যের রেফারেন্সগুলির সাথে কাজ করার সক্ষমতা সরবরাহ করে তবে এটি তাদের মূল উদ্দেশ্যকে উপেক্ষা করে, যা স্থানে অবজেক্টগুলি নির্মাণ করে।


4
" স্ট্যান্ডার্ডে এমপ্লেস যুক্ত করার জন্য" কোনও প্রয়োজন "ছিল না।" এটা স্পষ্টতই মিথ্যা। emplace()এমন কোনও উপাদান .োকানোর একমাত্র উপায় যা অনুলিপি করা বা সরানো যায় না। (এবং হ্যাঁ, সম্ভবত, এমন কাউকে সর্বাধিক দক্ষতার সাথে সন্নিবেশ করানোর জন্য যার অনুলিপি এবং মুভ কনস্ট্রাক্টরগুলির নির্মাণের চেয়ে অনেক বেশি ব্যয় হয়, যদি এরকম কোনও জিনিস থাকে) এটিও মনে হয় আপনি ধারণাটি ভুল পেয়েছেন: এটি " [গ্রহণ] মূল্যের রেফারেন্সের সুবিধা নিয়ে নয়" আপনি ইতিমধ্যে তৈরি প্রকৃত অবজেক্ট ব্যবহার করতে "; কোন বস্তু এখনো তৈরি করা হয়, & আপনি ফরোয়ার্ড mapআর্গুমেন্ট এটা নিজে এর ভিতরে এটি তৈরি করা প্রয়োজন। আপনি আপত্তি না।
আন্ডারস্কোর_২

10

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

এখানে একটি উদাহরণ এখানে প্রদর্শিত:

#include <vector>

struct foo
{
    explicit foo(int);
};

int main()
{
    std::vector<foo> v;

    v.emplace(v.end(), 10);      // Works
    //v.insert(v.end(), 10);     // Error, not explicit
    v.insert(v.end(), foo(10));  // Also works
}

এটি স্বীকারোক্তভাবে একটি খুব নির্দিষ্ট বিশদ, তবে আপনি যখন ব্যবহারকারী-সংজ্ঞায়িত রূপান্তরগুলির শৃঙ্খলাগুলি পরিচালনা করছেন তখন এটি মনে রাখা উচিত।


কল্পনা করুন যে ফু এর জন্য কর্টারে একের চেয়ে দুটি ইনট লাগবে। আপনি কি এই কলটি ব্যবহার করতে পারবেন? v.emplace(v.end(), 10, 10); ... অথবা আপনি এখন ব্যবহার করতে হবে হবে: v.emplace(v.end(), foo(10, 10) ); ?
কাইতাইন

আমার এখনই সংকলক অ্যাক্সেস নেই, তবে আমি ধরে নেব যে এর অর্থ উভয় সংস্করণ কাজ করবে। emplaceক্লাস ব্যবহারের জন্য আপনি দেখতে প্রায় সব উদাহরণ যা একটি একক প্যারামিটার নেয়। আইএমও এটি উদাহরণস্বরূপ একাধিক পরামিতি ব্যবহার করা হত তবে এম্প্লেসের বৈচিত্র্যসূচক সিনট্যাক্সের প্রকৃতিটিকে আরও ভাল করে তুলবে।
কাইতাইন

9

নিম্নলিখিত কোডটি আপনাকে "বড় চিত্রের ধারণা" বুঝতে কীভাবে insert()পৃথক হতে পারে তা বুঝতে সহায়তা করতে পারে emplace():

#include <iostream>
#include <unordered_map>
#include <utility>

//Foo simply outputs what constructor is called with what value.
struct Foo {
  static int foo_counter; //Track how many Foo objects have been created.
  int val; //This Foo object was the val-th Foo object to be created.

  Foo() { val = foo_counter++;
    std::cout << "Foo() with val:                " << val << '\n';
  }
  Foo(int value) : val(value) { foo_counter++;
    std::cout << "Foo(int) with val:             " << val << '\n';
  }
  Foo(Foo& f2) { val = foo_counter++;
    std::cout << "Foo(Foo &) with val:           " << val
              << " \tcreated from:      \t" << f2.val << '\n';
  }
  Foo(const Foo& f2) { val = foo_counter++;
    std::cout << "Foo(const Foo &) with val:     " << val
              << " \tcreated from:      \t" << f2.val << '\n';
  }
  Foo(Foo&& f2) { val = foo_counter++;
    std::cout << "Foo(Foo&&) moving:             " << f2.val
              << " \tand changing it to:\t" << val << '\n';
  }
  ~Foo() { std::cout << "~Foo() destroying:             " << val << '\n'; }

  Foo& operator=(const Foo& rhs) {
    std::cout << "Foo& operator=(const Foo& rhs) with rhs.val: " << rhs.val
              << " \tcalled with lhs.val = \t" << val
              << " \tChanging lhs.val to: \t" << rhs.val << '\n';
    val = rhs.val;
    return *this;
  }

  bool operator==(const Foo &rhs) const { return val == rhs.val; }
  bool operator<(const Foo &rhs)  const { return val < rhs.val;  }
};

int Foo::foo_counter = 0;

//Create a hash function for Foo in order to use Foo with unordered_map
namespace std {
   template<> struct hash<Foo> {
       std::size_t operator()(const Foo &f) const {
           return std::hash<int>{}(f.val);
       }
   };
}

int main()
{
    std::unordered_map<Foo, int> umap;  
    Foo foo0, foo1, foo2, foo3;
    int d;

    //Print the statement to be executed and then execute it.

    std::cout << "\numap.insert(std::pair<Foo, int>(foo0, d))\n";
    umap.insert(std::pair<Foo, int>(foo0, d));
    //Side note: equiv. to: umap.insert(std::make_pair(foo0, d));

    std::cout << "\numap.insert(std::move(std::pair<Foo, int>(foo1, d)))\n";
    umap.insert(std::move(std::pair<Foo, int>(foo1, d)));
    //Side note: equiv. to: umap.insert(std::make_pair(foo1, d));

    std::cout << "\nstd::pair<Foo, int> pair(foo2, d)\n";
    std::pair<Foo, int> pair(foo2, d);

    std::cout << "\numap.insert(pair)\n";
    umap.insert(pair);

    std::cout << "\numap.emplace(foo3, d)\n";
    umap.emplace(foo3, d);

    std::cout << "\numap.emplace(11, d)\n";
    umap.emplace(11, d);

    std::cout << "\numap.insert({12, d})\n";
    umap.insert({12, d});

    std::cout.flush();
}

আমি যে আউটপুট পেয়েছিলাম তা হ'ল:

Foo() with val:                0
Foo() with val:                1
Foo() with val:                2
Foo() with val:                3

umap.insert(std::pair<Foo, int>(foo0, d))
Foo(Foo &) with val:           4    created from:       0
Foo(Foo&&) moving:             4    and changing it to: 5
~Foo() destroying:             4

umap.insert(std::move(std::pair<Foo, int>(foo1, d)))
Foo(Foo &) with val:           6    created from:       1
Foo(Foo&&) moving:             6    and changing it to: 7
~Foo() destroying:             6

std::pair<Foo, int> pair(foo2, d)
Foo(Foo &) with val:           8    created from:       2

umap.insert(pair)
Foo(const Foo &) with val:     9    created from:       8

umap.emplace(foo3, d)
Foo(Foo &) with val:           10   created from:       3

umap.emplace(11, d)
Foo(int) with val:             11

umap.insert({12, d})
Foo(int) with val:             12
Foo(const Foo &) with val:     13   created from:       12
~Foo() destroying:             12

~Foo() destroying:             8
~Foo() destroying:             3
~Foo() destroying:             2
~Foo() destroying:             1
~Foo() destroying:             0
~Foo() destroying:             13
~Foo() destroying:             11
~Foo() destroying:             5
~Foo() destroying:             10
~Foo() destroying:             7
~Foo() destroying:             9

লক্ষ্য করুন:

  1. একটি unordered_mapসর্বদা অভ্যন্তরীণভাবে কীগুলি হিসাবে Fooবস্তুগুলিকে (এবং না, বলুন Foo *) সংরক্ষণ করে, যা সর্বনাশ হয়ে গেলে সমস্ত ধ্বংস unordered_mapহয়। এখানে, unordered_mapঅভ্যন্তরীণ কীগুলি হ'ল 13, 11, 5, 10, 7 এবং 9।

    • প্রযুক্তিগতভাবে, আমাদের unordered_mapপ্রকৃতপক্ষে std::pair<const Foo, int>অবজেক্টগুলি সঞ্চয় করে, যার ফলে Fooবস্তুগুলি সংরক্ষণ করে store তবে "বিগ পিকচার আইডিয়া" বুঝতে কীভাবে emplace()আলাদা হয় insert()(নীচে হাইলাইট করা বাক্স দেখুন), এই বিষয়টিকে সম্পূর্ণ প্যাসিভ হিসাবে অস্থায়ীভাবে কল্পনা করা ঠিক আছে std::pair। আপনি যখন এই "বড় চিত্রের ধারণাটি" বুঝতে পারবেন, তখন ব্যাকআপ নেওয়া এবং বুঝতে হবে কীভাবে এই মধ্যস্থতাকারী std::pairঅবজেক্টটির ব্যবহারের মাধ্যমে unordered_mapসূক্ষ্ম, তবে গুরুত্বপূর্ণ, প্রযুক্তিগত দিকগুলি উপস্থাপিত হয়।
  2. প্রতিটি ঢোকানো foo0, foo1এবং foo2এক 2 কল প্রয়োজনীয় Foo'র কপি / পদক্ষেপ কনস্ট্রাকটর এবং 2 কল Fooএর বিনাশকারী (যেমন আমি এখন বর্ণনা):

    ক। প্রতিটি সন্নিবেশ করা foo0এবং foo1একটি অস্থায়ী বস্তু তৈরি করা ( foo4এবং foo6যথাক্রমে) যার ধ্বংসকারীকে ততক্ষণে সন্নিবেশ সম্পন্ন হওয়ার পরে ডাকা হয়েছিল। এছাড়াও, আনর্ডর্ডার্ড_ম্যাপটির অভ্যন্তরীণ Fooগুলি (যা Foo5 এবং are এর হয় ) যখন বিনা নিয়ন্ত্রিত_ম্যাপটি ধ্বংস হয়েছিল তখন তাদের ধ্বংসকারীদেরও ডেকেছিল।

    খ। সন্নিবেশ করার জন্য foo2, আমরা পরিবর্তে প্রথমে স্পষ্টভাবে একটি অস্থায়ী জোড় বস্তু তৈরি করেছি (যাকে বলা হয় pair), যাকে Foo'অনুলিপি নির্মাণকারী foo2(এর foo8অভ্যন্তরীণ সদস্য হিসাবে তৈরি করা pair) বলা হয়। তারপরে আমরা insert()এই জুটিটি সম্পাদনা করেছি, যার ফলে unordered_mapঅনুলিপিটি অনুলিপিটি অনুলিপি করতে (আবার foo8) তার নিজস্ব অভ্যন্তরীণ অনুলিপি তৈরি করতে ( foo9)। সঙ্গে fooগুলি 0 এবং 1, শেষ ফলাফল শুধু পার্থক্য হচ্ছে যে এই সন্নিবেশ জন্য দুটি বিনাশকারী কল ছিল foo8এর বিনাশকারী শুধুমাত্র যখন আমরা শেষে পৌঁছে গেছেন বলা হত main()বদলে পরে অবিলম্বে বলা হচ্ছে insert()সমাপ্ত।

  3. বিস্তৃতকরণের foo3ফলে কেবলমাত্র 1 টি অনুলিপি / মুভ কনস্ট্রাক্টর কল ( foo10অভ্যন্তরীণভাবে তৈরি করা unordered_map) এবং কেবলমাত্র 1 টি কলটির Fooডেস্ট্রাক্টরকে কল হয়েছিল । (আমি পরে এটি ফিরে পাবেন)।

  4. জন্য foo11, আমরা সরাসরি পূর্ণসংখ্যা 11 গৃহীত emplace(11, d)যাতে unordered_mapকল করবে Foo(int)কন্সট্রাকটর যখন মৃত্যুদন্ড তার মধ্যে emplace()পদ্ধতি। (2) এবং (3) এর বিপরীতে, এটি করার জন্য আমাদের কিছু প্রি-এক্সিটিং fooঅবজেক্টের প্রয়োজনও ছিল না। গুরুত্বপূর্ণভাবে, লক্ষ্য করুন যে কোনও Fooকনস্ট্রাক্টরের কাছে কেবল 1 কল এসেছে (যা তৈরি হয়েছিল foo11)।

  5. আমরা তখন পূর্ণসংখ্যাটি 12 থেকে সরাসরি পাস করেছি insert({12, d})emplace(11, d)(যা প্রত্যাহার করে কেবল কোনও Fooকনস্ট্রাক্টরের কাছে কেবল 1 কল হয়েছিল ) এর বিপরীতে , এই কলটির insert({12, d})ফলে Fooকনস্ট্রাক্টরকে দুটি কল (তৈরি foo12এবং তৈরি করা হয়েছিল foo13)।

এটি দেখায় যে প্রধান "বড় চিত্র" এর মধ্যে পার্থক্য কী insert()এবং emplace():

যদিও insert() প্রায়শই ব্যবহারের জন্য Fooকোনও ক্ষেত্রের main()স্কোপ (কোনও অনুলিপি বা সরানো অনুসরণ) এর কোনও অস্তিত্বের নির্মাণ বা অস্তিত্বের প্রয়োজন হয় , তবে যদি emplace()কোনও Fooকনস্ট্রাক্টরের কাছে কোনও কল সম্পূর্ণভাবে অভ্যন্তরীণভাবে করা হয় unordered_map(অর্থাত্ emplace()পদ্ধতির সংজ্ঞাটির আওতার ভিতরে )। যুক্তি (গুলি) কি যে আপনি পাস জন্য emplace()সরাসরি একটি পাঠিয়ে দেওয়া হয় Fooমধ্যে কন্সট্রাকটর কল unordered_map::emplace()এর সংজ্ঞা (ঐচ্ছিক অতিরিক্ত বিবরণ: যেখানে এই নবনির্মিত বস্তুর অবিলম্বে এক মধ্যে অন্তর্ভুক্ত করা হয় unordered_mapএর তাই সদস্য ভেরিয়েবল কোনো বিনাশকারী যখন বলা হয় এক্সিকিউশন পাতা emplace()এবং কোনও সরানো বা অনুলিপি নির্মাণকারীদের ডাকা হয় না)।

দ্রষ্টব্য: উপরে " প্রায় সবসময় " প্রায় জন্য কারণ নীচে I তে ব্যাখ্যা করা হয়েছে।

  1. অব্যাহত: কেন কলিং umap.emplace(foo3, d)নামক Foo'র অ const কপি কন্সট্রাকটর অনুসরণ করছে: আমরা ব্যবহার করছেন যেহেতু emplace(), কম্পাইলার জানে foo3(ক অ const Fooবস্তুর) কিছু একটি আর্গুমেন্ট হতে বোঝানো হয় Fooকন্সট্রাকটর। এই ক্ষেত্রে, সবচেয়ে ফিটিং Fooকনস্ট্রাক্টর হ'ল নন-কনস্ট্যান্ট কপি কনস্ট্রাক্টর Foo(Foo& f2)। না কেন এটি umap.emplace(foo3, d)অনুলিপি নির্মাণকারী বলা হয় umap.emplace(11, d)

উপসংহার:

I. নোট করুন যে একটি ওভারলোড insert()আসলে এর সমতুল্য emplace()এই cppreferences.com পৃষ্ঠায় বর্ণিত হিসাবে , ওভারলোড template<class P> std::pair<iterator, bool> insert(P&& value)(যা এই সিপ্রেফারেন্স ডটকম পৃষ্ঠায় ওভারলোড (2) এর insert()সমতুল্য) emplace(std::forward<P>(value))

২। এখান থেকে কোথায় যাব?

ক। জন্য উপরের সোর্স কোড এবং অধ্যয়ন ডকুমেন্টেশন নিয়ে খেলা করুন insert()(যেমন এখানে ) এবং emplace()(যেমন এখানে ) যে অনলাইন খুঁজে পাওয়া যায়নি। আপনি যদি গ্রহন বা নেটবিয়ানের মতো কোনও আইডিই ব্যবহার করেন তবে আপনি সহজেই আপনার আইডিই পেতে পারেন যে কোনটির ওভারলোড insert()বা emplace()বলা হচ্ছে (গ্রহনে, কেবলমাত্র একটি মাউসের কার্সারকে এক সেকেন্ডের জন্য ফাংশন কলের উপরে স্থির রাখুন)। চেষ্টা করার জন্য এখানে আরও কিছু কোড রয়েছে:

std::cout << "\numap.insert({{" << Foo::foo_counter << ", d}})\n";
umap.insert({{Foo::foo_counter, d}});
//but umap.emplace({{Foo::foo_counter, d}}); results in a compile error!

std::cout << "\numap.insert(std::pair<const Foo, int>({" << Foo::foo_counter << ", d}))\n";
umap.insert(std::pair<const Foo, int>({Foo::foo_counter, d}));
//The above uses Foo(int) and then Foo(const Foo &), as expected. but the
// below call uses Foo(int) and the move constructor Foo(Foo&&). 
//Do you see why?
std::cout << "\numap.insert(std::pair<Foo, int>({" << Foo::foo_counter << ", d}))\n";
umap.insert(std::pair<Foo, int>({Foo::foo_counter, d}));
//Not only that, but even more interesting is how the call below uses all 
// three of Foo(int) and the Foo(Foo&&) move and Foo(const Foo &) copy 
// constructors, despite the below call's only difference from the call above 
// being the additional { }.
std::cout << "\numap.insert({std::pair<Foo, int>({" << Foo::foo_counter << ", d})})\n";
umap.insert({std::pair<Foo, int>({Foo::foo_counter, d})});


//Pay close attention to the subtle difference in the effects of the next 
// two calls.
int cur_foo_counter = Foo::foo_counter;
std::cout << "\numap.insert({{cur_foo_counter, d}, {cur_foo_counter+1, d}}) where " 
  << "cur_foo_counter = " << cur_foo_counter << "\n";
umap.insert({{cur_foo_counter, d}, {cur_foo_counter+1, d}});

std::cout << "\numap.insert({{Foo::foo_counter, d}, {Foo::foo_counter+1, d}}) where "
  << "Foo::foo_counter = " << Foo::foo_counter << "\n";
umap.insert({{Foo::foo_counter, d}, {Foo::foo_counter+1, d}});


//umap.insert(std::initializer_list<std::pair<Foo, int>>({{Foo::foo_counter, d}}));
//The call below works fine, but the commented out line above gives a 
// compiler error. It's instructive to find out why. The two calls
// differ by a "const".
std::cout << "\numap.insert(std::initializer_list<std::pair<const Foo, int>>({{" << Foo::foo_counter << ", d}}))\n";
umap.insert(std::initializer_list<std::pair<const Foo, int>>({{Foo::foo_counter, d}}));

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

খ। আপনি পরিবর্তে অন্য কোনও ধারক শ্রেণি (যেমন std::setবা std::unordered_multiset) ব্যবহার করলে কী হয় তা দেখুন std::unordered_map

গ। এখন Gooএকটিতে (যেমন পরিবর্তে ব্যবহার করুন ) ব্যাপ্তি প্রকারের Fooপরিবর্তে একটি অবজেক্ট (কেবলমাত্র একটি নাম পরিবর্তিত অনুলিপি ) ব্যবহার করুন এবং দেখুন এবং কয়টি এবং কোন নির্মাণকারীকে ডাকা হয়েছে তা দেখুন। (স্পোলার: একটি প্রভাব রয়েছে তবে এটি খুব নাটকীয় নয়))intunordered_mapunordered_map<Foo, Goo>unordered_map<Foo, int>Goo


0

কার্যকারিতা বা আউটপুট হিসাবে, তারা উভয় একই।

উভয় বৃহত মেমরির জন্য, অবজেক্ট এমপ্লেস মেমরি-অপ্টিমাইজড যা অনুলিপি নির্মাণকারী ব্যবহার করে না

সাধারণ বিস্তারিত ব্যাখ্যার জন্য https://medium.com/@sandywits/all-about-emplace-in-c-71fd15e06e44

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