যুক্তি নির্ভর নির্ভরতা কী সম্পর্কে কিছু ভাল ব্যাখ্যা কী? অনেকে এটিকে কোয়েনিগ লুকআপও বলে থাকেন।
সাধারণত আমি জানতে চাই:
- কেন এটি একটি ভাল জিনিস?
- কেন এটা খারাপ জিনিস?
- এটা কিভাবে কাজ করে?
std::cout << "Hello world";
সংকলন করবে না
যুক্তি নির্ভর নির্ভরতা কী সম্পর্কে কিছু ভাল ব্যাখ্যা কী? অনেকে এটিকে কোয়েনিগ লুকআপও বলে থাকেন।
সাধারণত আমি জানতে চাই:
std::cout << "Hello world";
সংকলন করবে না
উত্তর:
কোইনিগ লুকআপ বা তর্ক নির্ভরতা অবলম্বন বর্ণনামূলকভাবে বর্ণনা করে যে সি ++ তে সংকলক দ্বারা কীভাবে অযোগ্য নামগুলি সন্ধান করা হয়।
সি ++ 11 স্ট্যান্ডার্ড § 3.4.2 / 1 বলেছেন:
যখন কোনও ফাংশন কলের (5.2.2) পোস্টফিক্স-এক্সপ্রেশনটি একটি অযোগ্য-আইডি হয়, তখন সাধারণ অসমর্থিত অনুসন্ধানের (3.4.1) সময় বিবেচনা করা হয়নি এমন অন্যান্য নেমস্পেসগুলি অনুসন্ধান করা যেতে পারে এবং সেই নামের জায়গাগুলিতে, নেমস্পেস-স্কোপ বন্ধু ফাংশন ঘোষণা ( ১১.৩) অন্যথায় দৃশ্যমান নয় এমনটি পাওয়া যেতে পারে। অনুসন্ধানে এই পরিবর্তনগুলি আর্গুমেন্টের ধরণের (এবং টেমপ্লেট টেম্পলেট আর্গুমেন্টগুলির জন্য, টেমপ্লেট আর্গুমেন্টের নামের জায়গার উপর) নির্ভর করে।
সহজ কথায় নিকোলাই জোসুটিস 1 বলেছেন :
ফাংশনের নেমস্পেসে যদি এক বা একাধিক আর্গুমেন্টের ধরণ সংজ্ঞায়িত করা হয় তবে আপনাকে ফাংশনগুলির জন্য নেমস্পেসের যোগ্যতা অর্জন করতে হবে না।
একটি সাধারণ কোড উদাহরণ:
namespace MyNamespace
{
class MyClass {};
void doSomething(MyClass);
}
MyNamespace::MyClass obj; // global object
int main()
{
doSomething(obj); // Works Fine - MyNamespace::doSomething() is called.
}
উপরের উদাহরণে তন্ন তন্ন একটি আছে using
-declaration অথবা একটি using
-directive কিন্তু এখনও কম্পাইলার সঠিকভাবে অযোগ্য নাম চিহ্নিত doSomething()
ফাংশন নামস্থান ঘোষণা যেমন MyNamespace
প্রয়োগের দ্বারা Koenig লুকআপ ।
অ্যালগরিদম কম্পাইলারকে কেবল স্থানীয় সুযোগের দিকে নজর না দিয়ে আর্গুমেন্টের টাইপযুক্ত নেমস্পেসগুলি দেখতে বলে। সুতরাং, উপরের কোডে, সংকলকটি আবিষ্কার করে যে বস্তুটি obj
, যা ফাংশনটির আর্গুমেন্ট doSomething()
, নাম স্পেসের সাথে সম্পর্কিত MyNamespace
। সুতরাং, এটি ঘোষণাপত্রটি সনাক্ত করতে সেই নামস্থানটি দেখছে doSomething()
।
উপরের সরল কোড উদাহরণ হিসাবে দেখানো হয়েছে, কোনিগ লুকআপ প্রোগ্রামারটিকে সুবিধার্থে এবং ব্যবহারের সহজতা সরবরাহ করে। কোয়েনিগ অনুসন্ধান ব্যতীত প্রোগ্রামারটিতে একটি ওভারহেড থাকবে, সম্পূর্ণরূপে using
যোগ্যতার নামগুলি বারবার নির্দিষ্ট করতে বা পরিবর্তে, অসংখ্য- বিবরণী ব্যবহার করতে হবে ।
কোয়েনিগ লুকের উপর অতিরিক্ত নির্ভরতা অর্থপূর্ণ সমস্যার কারণ হতে পারে এবং কখনও কখনও প্রোগ্রামারকে রক্ষা করতে পারে।
উদাহরণটি বিবেচনা করুন std::swap
, যা দুটি মান অদলবদল করার জন্য একটি আদর্শ গ্রন্থাগার অ্যালগরিদম। কোনিগ লুকআপের সাথে এই অ্যালগরিদমটি ব্যবহার করার সময় কাউকে সতর্ক থাকতে হবে কারণ:
std::swap(obj1,obj2);
এর মতো আচরণটি নাও দেখাতে পারে:
using std::swap;
swap(obj1, obj2);
এডিএল দিয়ে, swap
ফাংশনের কোন সংস্করণটি কল হয় এটি আর্গুমেন্টের পাস করা যুক্তির নামের জায়গার উপর নির্ভর করবে।
একটি নামস্থান অস্তিত্ব থাকে A
এবং যদি A::obj1
, A::obj2
& A::swap()
অস্তিত্ব তারপর দ্বিতীয় উদাহরণ একটি কল পরিণাম ডেকে আনবে A::swap()
, যা কি ব্যবহারকারী চেয়েছিলেন নাও হতে পারে।
তদ্ব্যতীত, যদি কোনও কারণে উভয় কারণে A::swap(A::MyClass&, A::MyClass&)
এবং std::swap(A::MyClass&, A::MyClass&)
সংজ্ঞায়িত হয় তবে প্রথম উদাহরণটি কল করবে std::swap(A::MyClass&, A::MyClass&)
তবে দ্বিতীয়টি সংকলন করবে না কারণ swap(obj1, obj2)
এটি অস্পষ্ট।
কারণ এটি প্রাক্তন এটিএন্ডটি এবং বেল ল্যাবস গবেষক এবং প্রোগ্রামার, অ্যান্ড্রু কোয়েনিং তৈরি করেছিলেন ।
স্ট্যান্ডার্ড সি ++ 03/11 [বেসিক.লুপআপ.আরজিডিপ]: 3.4.2 তর্ক-নির্ভর নাম অনুসন্ধান।
1 জোসেট্টিসের বই, দ্য সি ++ স্ট্যান্ডার্ড লাইব্রেরি: একটি টিউটোরিয়াল এবং রেফারেন্সে কোনেইগ লুকের সংজ্ঞাটি সংজ্ঞায়িত হয়েছে।
std::swap
আসলে এটি করতে হবে যেহেতু একমাত্র বিকল্প std::swap
হ'ল আপনার A
শ্রেণীর জন্য টেম্পলেট ফাংশন স্পষ্ট স্পেশালাইজেশন যুক্ত করা। তবুও যদি আপনার A
শ্রেণি নিজেই একটি টেম্পলেট হয় তবে এটি স্পষ্টত বিশেষত্বের চেয়ে আংশিক বিশেষত্ব হবে। এবং টেম্পলেট ফাংশনটির আংশিক বিশেষত্ব অনুমোদিত নয়। এর ওভারলোড যুক্ত করা std::swap
বিকল্প হবে তবে এটি স্পষ্টভাবে নিষিদ্ধ করা হয়েছে (আপনি std
নামের জায়গাতে জিনিস যোগ করতে পারেন না )। সুতরাং ADL এর একমাত্র উপায় std::swap
।
std::swap()
কিছুটা পিছনের দিকে মনে হচ্ছে। আমি সমস্যা না যখন আশা std::swap()
, টাইপ জমিদার নির্দিষ্ট বদলে নির্বাচিত হয় A::swap()
। উদাহরণটি std::swap(A::MyClass&, A::MyClass&)
বিভ্রান্তিকর বলে মনে হচ্ছে। যেহেতু std
কখনও কোনও ব্যবহারকারীর জন্য নির্দিষ্ট ওভারলোড না থাকায় আমি মনে করি এটি দুর্দান্ত উদাহরণ নয়।
MyNamespace::doSomething
, কেবল নয় ::doSomething
।
Koenig লুকআপ সালে একটি ফাংশন তার নামস্থান উল্লেখ না করে বলা হয়, তাহলে একটি ফাংশন নাম এছাড়াও নামস্থান (গুলি) যা যুক্তি (গুলি) ধরণ সংজ্ঞায়িত করা হয় অনুসন্ধান করেছি। এজন্য এটি সংক্ষেপে এডিএল হিসাবে যুক্তি-নির্ভরশীল নাম লুকআপ নামেও পরিচিত ।
এটি কোনিগ লুকআপের কারণে, আমরা এটি লিখতে পারি:
std::cout << "Hello World!" << "\n";
অন্যথায়, আমাদের লিখতে হবে:
std::operator<<(std::operator<<(std::cout, "Hello World!"), "\n");
যা সত্যিই অনেক বেশি টাইপিং এবং কোডটি দেখতে সত্যিই কুৎসিত দেখাচ্ছে!
অন্য কথায়, কোনিগ লুকআপের অনুপস্থিতিতে, এমনকি একটি হ্যালো ওয়ার্ল্ড প্রোগ্রাম জটিল দেখায়।
std::cout
এটি ফাংশনটির জন্য একটি যুক্তি, যা এডিএল সক্ষম করতে যথেষ্ট। আপনি কি তা খেয়াল করেছেন?
ostream<<
(এটি কী আর্গুমেন্ট হিসাবে গ্রহণ করে এবং এটি কী ফিরে আসে) হিসাবে। 2) পুরোপুরি যোগ্য নাম (যেমন std::vector
বা std::operator<<
)। 3) আর্গুমেন্ট ডিপেন্ডেন্ট লুকআপের আরও বিশদ অধ্যয়ন।
std::endl
যুক্তি হিসাবে গ্রহণ করতে পারে যে ফাংশন , আসলে একটি সদস্য ফাংশন। যাইহোক, আমি যদি এর "\n"
পরিবর্তে ব্যবহার করি std::endl
তবে আমার উত্তরটি সঠিক। মন্তব্যের জন্য ধন্যবাদ.
f(a,b)
পূজা একটি বিনামূল্যে ফাংশন। সুতরাং ক্ষেত্রে std::operator<<(std::cout, std::endl);
, এই জাতীয় ফাংশন নেই যা std::endl
দ্বিতীয় যুক্তি হিসাবে গ্রহণ করে । এটি সদস্য ফাংশন যা std::endl
যুক্তি হিসাবে গ্রহণ করে এবং যার জন্য আপনাকে লিখতে হবে std::cout.operator<<(std::endl);
। এবং যেহেতু একটি নিখরচায় ফাংশন রয়েছে যা char const*
দ্বিতীয় তর্ক হিসাবে "\n"
কাজ করে; '\n'
পাশাপাশি কাজ করবে।
হতে পারে কেন এটি দিয়ে শুরু করা ভাল এবং কেবল তারপরে কীভাবে চলে যান।
যখন নেমস্পেসগুলি চালু করা হয়েছিল, তখন ধারণাটি ছিল যে সমস্ত কিছু নেমস্পেসে সংজ্ঞায়িত করা উচিত, যাতে পৃথক গ্রন্থাগারগুলি একে অপরের সাথে হস্তক্ষেপ না করে। তবে এটি অপারেটরদের সাথে একটি সমস্যা চালু করেছে। নিম্নলিখিত কোডে উদাহরণস্বরূপ দেখুন:
namespace N
{
class X {};
void f(X);
X& operator++(X&);
}
int main()
{
// define an object of type X
N::X x;
// apply f to it
N::f(x);
// apply operator++ to it
???
}
অবশ্যই আপনি লিখতে পারতেন N::operator++(x)
, তবে এটি অপারেটর ওভারলোডিংয়ের পুরো পয়েন্টকে পরাস্ত করতে পারে। অতএব এমন একটি সমাধান খুঁজে বের করতে হয়েছিল যা সংস্থাপকটি operator++(X&)
এটির সুযোগ না থাকা সত্ত্বেও এটি সন্ধান করতে দেয়। অন্যদিকে, এটি এখনও অন্য কোনও সন্ধান করা উচিত নয়operator++
অন্য কোনও সম্পর্কিত, সম্পর্কিত সম্পর্কহীন, যা কলটিকে দ্বিধাগ্রস্থ করতে পারে তার মধ্যে অন্য সংজ্ঞা পাওয়া (এই সাধারণ উদাহরণে আপনি অস্পষ্টতা পাবেন না, তবে আরও জটিল উদাহরণে, আপনি সম্ভবত)) সমাধানটি আর্গুমেন্ট ডিপেন্ডেন্ট লুকআপ (এডিএল) বলা হয়েছিল, কারণ অনুসন্ধানটি আর্গুমেন্টের উপর নির্ভর করে (আরও সঠিকভাবে যুক্তির ধরণে)। যেহেতু স্কিমটি অ্যান্ড্রু আর কোয়েনিগ আবিষ্কার করেছিলেন, তাই এটি প্রায়শই কোয়েনিগ লুকআপও বলা হয়।
কৌতুকটি হ'ল ফাংশন কলগুলির জন্য, সাধারণ নাম অনুসন্ধানের পাশাপাশি (যা ব্যবহারের বিন্দুতে স্কোপগুলিতে নামগুলি খুঁজে পায়), ফাংশনে প্রদত্ত যে কোনও আর্গুমেন্টের ধরণের স্কোপগুলিতে একটি দ্বিতীয় অনুসন্ধান করা হয়। সুতরাং উপরোক্ত উদাহরণের, যদি আপনি লিখতে x++
প্রধান, এটা জন্য দেখায় operator++
না শুধুমাত্র বিশ্বব্যাপী সুযোগ কিন্তু অতিরিক্ত সুযোগ যেখানে ধরনের x
, N::X
, সংজ্ঞায়িত করা হয়, অর্থাৎ namespace N
। এবং সেখানে এটি একটি মিল খুঁজে পেয়েছে operator++
এবং তাই x++
কেবল কাজ করে। operator++
অন্য নাম-স্পেসে সংজ্ঞায়িত আরেকটি , বলুন N2
, তবে খুঁজে পাওয়া যাবে না। যেহেতু এডিএল নামস্থানগুলিতে সীমাবদ্ধ নয়, আপনি ভিতরে f(x)
পরিবর্তে ব্যবহার করতে পারেন ।N::f(x)
main()
আমার মতে এটি সম্পর্কে সমস্ত কিছুই ভাল নয়। সংকলক বিক্রেতারা সহ লোকেরা এর মাঝে মাঝে দুর্ভাগ্যজনক আচরণের কারণে এটি অপমান করে চলেছে।
সিএড ++ 11-এ রেঞ্জের লুপের একটি বড় ওভারহোলের জন্য এডিএল দায়বদ্ধ। এডিএল কখনও কখনও অনিচ্ছাকৃত প্রভাব ফেলতে পারে তা বুঝতে, বিবেচনা করুন যে আর্গুমেন্টগুলি সংজ্ঞায়িত করা হয়েছে কেবল সেই স্থানের ক্ষেত্রগুলিকেই নয়, সেই যুক্তিগুলির প্যারামিটার ধরণের পয়েন্টার ধরণের / পয়েন্টি ধরণের পয়েন্টার ধরণের পরামিতিগুলি , এবং তাই আরও অনেক কিছু।
বুস্ট ব্যবহার করে একটি উদাহরণ
std::vector<boost::shared_ptr<int>> v;
auto x = begin(v);
ব্যবহারকারী বুস্ট.রেঞ্জ লাইব্রেরি ব্যবহার করলে এটি একটি দ্ব্যর্থহীনতার ফলস্বরূপ, কারণ উভয়ই std::begin
পাওয়া যায় (ব্যবহার করে ADL দ্বারা std::vector
) এবং boost::begin
পাওয়া যায় (ADL ব্যবহার করে boost::shared_ptr
)।
std::begin
নেমস্পেসের অস্পষ্টতা সাফ করে।