নিম্নলিখিত কোডটি আপনাকে "বড় চিত্রের ধারণা" বুঝতে কীভাবে 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
লক্ষ্য করুন:
একটি 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
সূক্ষ্ম, তবে গুরুত্বপূর্ণ, প্রযুক্তিগত দিকগুলি উপস্থাপিত হয়।
প্রতিটি ঢোকানো foo0
, foo1
এবং foo2
এক 2 কল প্রয়োজনীয় Foo
'র কপি / পদক্ষেপ কনস্ট্রাকটর এবং 2 কল Foo
এর বিনাশকারী (যেমন আমি এখন বর্ণনা):
ক। প্রতিটি সন্নিবেশ করা foo0
এবং foo1
একটি অস্থায়ী বস্তু তৈরি করা ( foo4
এবং foo6
যথাক্রমে) যার ধ্বংসকারীকে ততক্ষণে সন্নিবেশ সম্পন্ন হওয়ার পরে ডাকা হয়েছিল। এছাড়াও, আনর্ডর্ডার্ড_ম্যাপটির অভ্যন্তরীণ Foo
গুলি (যা Foo
5 এবং are এর হয় ) যখন বিনা নিয়ন্ত্রিত_ম্যাপটি ধ্বংস হয়েছিল তখন তাদের ধ্বংসকারীদেরও ডেকেছিল।
খ। সন্নিবেশ করার জন্য foo2
, আমরা পরিবর্তে প্রথমে স্পষ্টভাবে একটি অস্থায়ী জোড় বস্তু তৈরি করেছি (যাকে বলা হয় pair
), যাকে Foo
'অনুলিপি নির্মাণকারী foo2
(এর foo8
অভ্যন্তরীণ সদস্য হিসাবে তৈরি করা pair
) বলা হয়। তারপরে আমরা insert()
এই জুটিটি সম্পাদনা করেছি, যার ফলে unordered_map
অনুলিপিটি অনুলিপিটি অনুলিপি করতে (আবার foo8
) তার নিজস্ব অভ্যন্তরীণ অনুলিপি তৈরি করতে ( foo9
)। সঙ্গে foo
গুলি 0 এবং 1, শেষ ফলাফল শুধু পার্থক্য হচ্ছে যে এই সন্নিবেশ জন্য দুটি বিনাশকারী কল ছিল foo8
এর বিনাশকারী শুধুমাত্র যখন আমরা শেষে পৌঁছে গেছেন বলা হত main()
বদলে পরে অবিলম্বে বলা হচ্ছে insert()
সমাপ্ত।
বিস্তৃতকরণের foo3
ফলে কেবলমাত্র 1 টি অনুলিপি / মুভ কনস্ট্রাক্টর কল ( foo10
অভ্যন্তরীণভাবে তৈরি করা unordered_map
) এবং কেবলমাত্র 1 টি কলটির Foo
ডেস্ট্রাক্টরকে কল হয়েছিল । (আমি পরে এটি ফিরে পাবেন)।
জন্য foo11
, আমরা সরাসরি পূর্ণসংখ্যা 11 গৃহীত emplace(11, d)
যাতে unordered_map
কল করবে Foo(int)
কন্সট্রাকটর যখন মৃত্যুদন্ড তার মধ্যে emplace()
পদ্ধতি। (2) এবং (3) এর বিপরীতে, এটি করার জন্য আমাদের কিছু প্রি-এক্সিটিং foo
অবজেক্টের প্রয়োজনও ছিল না। গুরুত্বপূর্ণভাবে, লক্ষ্য করুন যে কোনও Foo
কনস্ট্রাক্টরের কাছে কেবল 1 কল এসেছে (যা তৈরি হয়েছিল foo11
)।
আমরা তখন পূর্ণসংখ্যাটি 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 তে ব্যাখ্যা করা হয়েছে।
- অব্যাহত: কেন কলিং
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
পরিবর্তে একটি অবজেক্ট (কেবলমাত্র একটি নাম পরিবর্তিত অনুলিপি ) ব্যবহার করুন এবং দেখুন এবং কয়টি এবং কোন নির্মাণকারীকে ডাকা হয়েছে তা দেখুন। (স্পোলার: একটি প্রভাব রয়েছে তবে এটি খুব নাটকীয় নয়))int
unordered_map
unordered_map<Foo, Goo>
unordered_map<Foo, int>
Goo