সংক্ষিপ্ত উত্তর: x
একটি নির্ভরশীল নাম করার জন্য , যাতে টেমপ্লেট প্যারামিটারটি জানা না যাওয়া অবধি ততক্ষণ অনুসন্ধান স্থগিত করা হয়।
দীর্ঘ উত্তর: যখন কোনও সংকলক কোনও টেম্পলেটটি দেখেন, তখন টেমপ্লেটের প্যারামিটারটি না দেখে তাৎক্ষণিকভাবে কিছু নির্দিষ্ট পরীক্ষা করা হবে। প্যারামিটারটি জানা না হওয়া অবধি অন্যরা পিছিয়ে যায়। একে দ্বি-পর্যায়ের সংকলন বলা হয়, এবং এমএসভিসি এটি করে না তবে এটি স্ট্যান্ডার্ডের দ্বারা প্রয়োজনীয় এবং অন্যান্য বড় সংকলকগুলি প্রয়োগ করেছেন। আপনি যদি পছন্দ করেন তবে সংকলকটিকে অবশ্যই টেমপ্লেটটি দেখার সাথে সাথেই এটি সংকলন করতে হবে (কোনও ধরণের অভ্যন্তরীণ পার্স গাছের উপস্থাপনায়), এবং ইনস্ট্যান্টেশনটি পরবর্তীকাল পর্যন্ত সংকলন স্থগিত করে।
টেমপ্লেটে নিজেই সঞ্চালিত চেকগুলির নির্দিষ্ট তাত্পর্য না করে, সংকলকটি টেমপ্লেটে কোডটির ব্যাকরণটি সমাধান করতে সক্ষম হতে হবে।
সি ++ (এবং সি) তে কোডের ব্যাকরণটি সমাধান করার জন্য আপনাকে মাঝে মাঝে কিছু জানতে হবে যে কোনও কিছু টাইপ কিনা or উদাহরণ স্বরূপ:
#if WANT_POINTER
typedef int A;
#else
int A;
#endif
static const int x = 2;
template <typename T> void foo() { A *x = 0; }
যদি ক একটি প্রকার হয়, যা একটি পয়েন্টার ঘোষণা করে (বিশ্বব্যাপী ছায়া ব্যতীত কোনও প্রভাব ছাড়াই x
)। যদি ক কোনও বস্তু হয়, তবে এটি গুণ (এবং কিছু অপারেটরকে এটি অবৈধভাবে চাপিয়ে দেওয়া অবৈধ, কোনও মূল্যকে নির্ধারণ করে)। যদি এটি ভুল হয় তবে এই ত্রুটিটি অবশ্যই প্রথম পর্যায়ে চিহ্নিত করা উচিত , এটি কোনও নির্দিষ্ট তাত্ক্ষণিকভাবে নয়, টেমপ্লেটে একটি ত্রুটি হওয়ার জন্য এটি স্ট্যান্ডার্ড দ্বারা সংজ্ঞায়িত করা হয় । এমনকি যদি টেমপ্লেটটি তাত্ক্ষণিকভাবে কখনও ইনস্ট্যান্ট হয় না, তবে এ যদি একটি হয় int
তবে উপরের কোডটি খারাপভাবে তৈরি হয়েছে এবং এটি অবশ্যই নির্ণয় করতে হবে, ঠিক যেমনটি foo
কোনও টেম্পলেট না হলেও এটি ছিল একটি সরল ফাংশন।
এখন, স্ট্যান্ডার্ডটি বলে যে নামগুলি টেমপ্লেট প্যারামিটারগুলির উপর নির্ভরশীল নয় তাদের অবশ্যই প্রথম পর্বের মধ্যে সমাধান করতে হবে A
here এখানে কোনও নির্ভরশীল নাম নয়, এটি নির্বিশেষে একই জিনিসটিকে বোঝায় T
। সুতরাং এটি প্রথম পর্বের সন্ধান এবং পরীক্ষা করার জন্য টেমপ্লেটটি সংজ্ঞায়িত করার আগে এটি সংজ্ঞায়িত করা দরকার।
T::A
টি এর উপর নির্ভর করে এমন একটি নাম হবে যা আমরা সম্ভবত প্রথম পর্বে জানতে পারি না এটি টাইপ কিনা। টাইপ যা শেষ পর্যন্ত হিসাবে ব্যবহৃত হবেT
ইনস্ট্যান্টেশন তা সম্ভবত এখনও সংজ্ঞায়িত হয় নি, এমনকি এটি যদি আমরা না জানতাম তবে আমাদের টেম্পলেট প্যারামিটার হিসাবে কোন ধরণের (গুলি) ব্যবহৃত হবে। কিন্তু আমাদের অসামান্য গঠনযুক্ত টেম্পলেটগুলির জন্য আমাদের মূল্যবান 1 ম চেকগুলি করতে ব্যাকরণটি সমাধান করতে হবে। তাই মান নির্ভরশীল নামের জন্য একটি নিয়ম আছে - কম্পাইলার অনুমান করতে হবে যে তারা অ ধরনের করছি, যদি না দিয়ে যোগ্যতাসম্পন্ন typename
যে তারা নির্দিষ্ট করতে হয় ধরনের, বা নির্দিষ্ট দ্ব্যর্থহীন প্রেক্ষিতে ব্যবহার করা হয়েছে। উদাহরণস্বরূপ template <typename T> struct Foo : T::A {};
, T::A
এটিকে বেস ক্লাস হিসাবে ব্যবহার করা হয় এবং তাই এটি স্পষ্টতই একটি প্রকার। যদি Foo
এমন কোনও ধরণের সাথে তাত্ক্ষণিকভাবে থাকে যা ডেটা সদস্য থাকেA
নেস্টেড টাইপ এ এর পরিবর্তে ইনস্ট্যান্টেশন (পর্ব ২) করার কোডটিতে এটি একটি ত্রুটি, টেমপ্লেটে ত্রুটি নয় (পর্ব 1)।
তবে নির্ভরশীল বেস শ্রেণীর সাথে শ্রেণিক টেম্পলেট সম্পর্কে কী বলা যায়?
template <typename T>
struct Foo : Bar<T> {
Foo() { A *x = 0; }
};
একটি নির্ভরশীল নাম নাকি? বেস ক্লাস সহ, কোনও নাম বেস ক্লাসে উপস্থিত হতে পারে। সুতরাং আমরা বলতে পারি যে এ একটি নির্ভরশীল নাম, এবং এটি একটি অ-টাইপ হিসাবে বিবেচনা করে। এটির অযাচিত প্রভাব ফেলতে পারে যে ফু এর প্রতিটি নাম নির্ভরশীল এবং তাই প্রতিটি প্রকার ফু-তে ব্যবহৃত (অন্তর্নির্মিত প্রকারগুলি ব্যতীত) যোগ্য হতে হবে। ফু এর অভ্যন্তরে, আপনাকে লিখতে হবে:
typename std::string s = "hello, world";
কারণ std::string
এটি একটি নির্ভরশীল নাম এবং অন্যথায় নির্দিষ্ট না করা হলে এটি একটি অ-টাইপ হিসাবে ধরে নেওয়া হবে। সেকি!
আপনার পছন্দসই কোড ( return x;
) এর অনুমতি দেওয়ার সাথে দ্বিতীয় সমস্যাটি হ'ল এমনকি যদি Bar
এটি সংজ্ঞায়িত করা হয় Foo
এবং সে সংজ্ঞায় x
কোনও সদস্য নাও হয় তবে পরে কেউ Bar
কোনও ধরণের জন্য বিশেষায়িতকরণ সংজ্ঞায়িত করতে পারে Baz
, যেমন Bar<Baz>
একটি ডেটা সদস্য রয়েছে x
এবং তারপরে ইনস্ট্যান্টিয়েট করতে পারে Foo<Baz>
। সুতরাং তাত্ক্ষণিকভাবে, আপনার টেম্পলেটটি বিশ্বব্যাপী ফিরে আসার পরিবর্তে ডেটা সদস্যকে ফিরিয়ে দেবে x
। অথবা বিপরীতভাবে যদি এর বুনিয়াদি টেমপ্লেট সংজ্ঞা Bar
ছিল x
, তারা একটি বিশেষজ্ঞতা এটা ছাড়া সংজ্ঞায়িত করতে পারে, এবং আপনার টেমপ্লেট একটি বিশ্বব্যাপী জন্য দেখাবেx
মধ্যে ফিরে যাওয়ার Foo<Baz>
। আমি মনে করি এটি আপনার যেমন সমস্যা হিসাবে ততটুকু অবাক এবং মন খারাপ হিসাবে বিবেচিত হয়েছিল, তবে এটি নিঃশব্দে আশ্চর্যজনক, একটি বিস্ময়কর ত্রুটি নিক্ষেপের বিরোধিতা হিসাবে।
এই সমস্যাগুলি এড়াতে, কার্যকরভাবে স্ট্যান্ডার্ডটি বলে যে ক্লাস টেম্পলেটগুলির নির্ভরশীল বেস ক্লাসগুলি স্পষ্টভাবে অনুরোধ না করা পর্যন্ত অনুসন্ধানের জন্য বিবেচনা করা হয় না। এটি সবকিছু নির্ভরশীল হওয়া থেকে থামায় কারণ এটি নির্ভরশীল বেসে পাওয়া যেতে পারে। এটির আপনি দেখতে পাচ্ছেন এমন অনাকাঙ্ক্ষিত প্রভাবও রয়েছে - আপনাকে বেস ক্লাস থেকে জিনিসগুলি যোগ্য করতে হবে বা এটি পাওয়া যায় নি। A
নির্ভরশীল করার জন্য তিনটি সাধারণ উপায় রয়েছে :
using Bar<T>::A;
ক্লাসে - A
এখন কিছু নির্দিষ্ট করে Bar<T>
, তাই নির্ভরশীল।
Bar<T>::A *x = 0;
ব্যবহারের সময়ে - আবার, A
অবশ্যই আছে Bar<T>
। এটি যেহেতু typename
ব্যবহার করা হয়নি, তাই এটি একটি খারাপ উদাহরণ, তবে এটি সম্ভবত একটি খারাপ উদাহরণ, তবে আমাদের ইনস্ট্যান্টেশন না হওয়া পর্যন্ত অপেক্ষা করতে হবে কিনাoperator*(Bar<T>::A, x)
কোনও মূল্যবোধ ফেরায় । কে জানে, সম্ভবত ...
this->A;
ব্যবহারের স্থানে - A
একজন সদস্য, সুতরাং এটি যদি না থাকে তবে Foo
এটি অবশ্যই বেস শ্রেণিতে থাকতে হবে, আবার মান বলে যে এটি নির্ভর করে।
দ্বি-পর্বের সংকলনটি খুব মজাদার এবং কঠিন এবং আপনার কোডে অতিরিক্ত ভার্চিয়াজের জন্য কিছু বিস্ময়কর প্রয়োজনীয়তার পরিচয় দেয়। তবে বরং গণতন্ত্রের মতো এটি সম্ভবত অন্য সবগুলি বাদ দিয়ে কাজ করার সবচেয়ে খারাপতম উপায়।
আপনি যুক্তিসঙ্গতভাবে তর্ক করতে পারেন যে আপনার উদাহরণে, বেস শ্রেণীর কোনও নেস্টেড টাইপ return x;
হলে x
তা বোঝা যায় না , সুতরাং ভাষাটি (ক) বলতে হবে যে এটি একটি নির্ভরশীল নাম এবং (২) এটিকে একটি অ-টাইপ হিসাবে আচরণ করবে এবং আপনার কোড ছাড়া কাজ করবেthis->
। আপনার ক্ষেত্রে প্রযোজ্য না এমন সমস্যার সমাধান থেকে আপনি জামানত ক্ষতির শিকার হয়েছেন, তবে এখনও আপনার বেস ক্লাসটি সেই ছায়া গ্লোবালগুলির অধীনে আপনার নাম অন্তর্ভুক্ত করার সম্ভাবনা রয়েছে, বা আপনার মনে হয়েছে এমন নাম নেই যা আপনি ভাবেননি the তাদের ছিল এবং পরিবর্তে একটি বিশ্বব্যাপী সন্ধান করা হচ্ছে।
আপনি সম্ভবত যুক্তি দিতে পারেন যে ডিফল্ট নির্ভরশীল নামগুলির জন্য বিপরীত হওয়া উচিত (ধরণের ধরণটি যদি কোনও বিষয় হিসাবে নির্দিষ্ট না করা হয়) বা ডিফল্টটিকে আরও প্রসঙ্গের সংবেদনশীল হওয়া উচিত (ইন std::string s = "";
, std::string
টাইপ হিসাবে পড়া যেতে পারে কারণ অন্য কিছু ব্যাকরণগত হয় না জ্ঞান, যদিও std::string *s = 0;
দ্ব্যর্থক)। আবার, আমি জানি না কীভাবে নিয়মগুলি সম্মত হয়েছিল। আমার অনুমান যে পাঠ্যের যে পৃষ্ঠাগুলির প্রয়োজনীয়তা হবে, প্রচুর নির্দিষ্ট নিয়ম তৈরির বিরুদ্ধে হ্রাস করা হবে যার জন্য প্রসঙ্গটি কোনও প্রকার গ্রহণ করে এবং কোনটি নন-টাইপ।