( আমার সি ++ 11 উত্তরের জন্য এখানে দেখুন )
একটি সি ++ প্রোগ্রামকে বিশ্লেষণ করার জন্য, সংকলকটির নির্দিষ্ট নামগুলি প্রকার কিনা তা জানা দরকার। নিম্নলিখিত উদাহরণটি দেখায় যে:
t * f;
এটি কীভাবে পার্স করা উচিত? অনেক ভাষার ক্ষেত্রে একটি সংকলককে কোনও পার্স করার জন্য কোনও নামটির অর্থ জানার প্রয়োজন হয় না এবং কোডের একটি লাইন কী কর্ম করে তা মূলত জানতে হয় know সি ++ এ, উপরেরগুলি তবে এর t
অর্থের উপর নির্ভর করে বিস্তৃত ব্যাখ্যা দিতে পারে । যদি এটি কোনও প্রকার হয়, তবে এটি পয়েন্টারের ঘোষণা হবে f
। তবে এটি কোনও ধরণের না হলে এটি একটি গুণ হবে। সুতরাং সি ++ স্ট্যান্ডার্ড অনুচ্ছেদে বলেছে (3/7):
কিছু নাম প্রকার বা টেমপ্লেট বোঝায়। সাধারণভাবে, যখনই কোনও নামের মুখোমুখি হয় তখন তা নির্ধারণ করা দরকার যে নামটি প্রোগ্রামটিতে থাকা প্রোগ্রামকে বিশ্লেষণ করার আগে এই নামগুলির মধ্যে এই সত্তাগুলির একটিকে চিহ্নিত করে কিনা। যে প্রক্রিয়া এটি নির্ধারণ করে তাকে নাম লুক্কুল বলা হয়।
কোনও সংকলক কোনও নামকে কী t::x
বোঝায়, কীভাবে t
কোনও টেম্পলেট ধরণের প্যারামিটারকে বোঝায়? x
কোনও স্ট্যাটিক ইনটেম সদস্য হতে পারে যা গুণিত হতে পারে বা সমানভাবে ভাল কোনও নেস্টেড ক্লাস বা টাইপডেফ হতে পারে যা কোনও ঘোষণায় ফল দিতে পারে। যদি কোনও নামের এই সম্পত্তি থাকে - যা প্রকৃত টেম্পলেট আর্গুমেন্টগুলি না জানা পর্যন্ত এটি সন্ধান করা যায় না - তবে একে নির্ভরযোগ্য নাম বলা হয় (এটি টেমপ্লেটের পরামিতিগুলির উপর "নির্ভর করে")।
ব্যবহারকারী কেবলমাত্র টেম্পলেটটি ইনস্ট্যান্ট না করা পর্যন্ত অপেক্ষা করার পরামর্শ দিতে পারেন:
ব্যবহারকারীর টেম্পলেটটি ইনস্ট্যান্ট না করা পর্যন্ত অপেক্ষা করুন এবং তারপরে এর আসল অর্থটি সন্ধান করুন t::x * f;
।
এটি কার্যকর হবে এবং সম্ভাব্য বাস্তবায়ন পদ্ধতির হিসাবে স্ট্যান্ডার্ড দ্বারা প্রকৃতপক্ষে অনুমোদিত। এই সংকলকগণ মূলত টেম্পলেটটির পাঠ্যটিকে একটি অভ্যন্তরীণ বাফারে অনুলিপি করে এবং কেবল যখন ইনস্ট্যান্টেশন প্রয়োজন হয় তখন তারা টেমপ্লেটটি বিশ্লেষণ করে এবং সংজ্ঞাটিতে ত্রুটিগুলি সনাক্ত করে। কিন্তু কোনও টেম্পলেট লেখকের ত্রুটিযুক্ত টেম্পলেটটির ব্যবহারকারীদের (দরিদ্র সহকর্মীদের) বিরক্ত করার পরিবর্তে, অন্যান্য বাস্তবায়নগুলি খুব শীঘ্রই টেমপ্লেটগুলি পরীক্ষা করতে পছন্দ করে এবং তাত্ক্ষণিক সংশোধন হওয়ার আগেই সংজ্ঞা হিসাবে ত্রুটি দেয়।
তাই সংকলককে বলার একটি উপায় থাকতে হবে যে নির্দিষ্ট নামগুলি প্রকার এবং নির্দিষ্ট নাম নয়।
"টাইপনেম" কীওয়ার্ড
উত্তরটি হ'ল: আমরা সিদ্ধান্ত নিই যে সংকলকটি কীভাবে এটি পার্স করবে। যদি t::x
কোনও নির্ভরশীল নাম হয়, তবে আমাদের typename
এটির একটি নির্দিষ্ট উপায়ে পার্স করার জন্য কম্পাইলারকে বলার দ্বারা এটির উপসর্গ করা দরকার । স্ট্যান্ডার্ড এ বলেছে (14.6 / 2):
কোনও নামটি টেম্পলেট ঘোষণার বা সংজ্ঞায় ব্যবহৃত হয় এবং এটি কোনও টেম্পলেট-প্যারামিটারের উপর নির্ভরশীল কোনও প্রকারের নাম না নেওয়াকে ধরে নেওয়া হয় যদি না প্রযোজ্য নাম অনুসন্ধানে কোনও প্রকারের নাম না পাওয়া যায় বা নামটি কীওয়ার্ড টাইপনেমের দ্বারা যোগ্য না হয়।
অনেকগুলি নাম রয়েছে যার typename
জন্য প্রয়োজনীয় নয়, কারণ সংকলকটি টেম্পলেট সংজ্ঞাতে প্রযোজ্য নাম অনুসন্ধানের সাহায্যে কোনও নির্মাণকে কীভাবে পার্স করতে হবে তা নির্ধারণ করতে পারে - উদাহরণস্বরূপ T *f;
, যখন T
কোনও টাইপ টেম্পলেট প্যারামিটার হয়। তবে t::x * f;
একটি ঘোষণাপত্র হওয়ার জন্য এটি অবশ্যই লিখতে হবে typename t::x *f;
। আপনি কীওয়ার্ডটি বাদ দিলে এবং নামটি একটি অ-টাইপ হিসাবে নেওয়া হয়, তবে যখন তাত্পর্যটি এটি কোনও প্রকারকে বোঝায় তখন সাধারণ ত্রুটি বার্তাগুলি সংকলক দ্বারা নির্গত হয়। কখনও কখনও, ত্রুটিটি ফলস্বরূপ সংজ্ঞা সময় দেওয়া হয়:
// t::x is taken as non-type, but as an expression the following misses an
// operator between the two names or a semicolon separating them.
t::x f;
বাক্য গঠনটি typename
কেবলমাত্র যোগ্য নামগুলির আগে অনুমতি দেয় - তাই এটি অনুমোদিত হিসাবে গ্রহণ করা হয় যে অযোগ্য নামগুলি সর্বদা এটির ক্ষেত্রে প্রকারগুলি উল্লেখ করতে পরিচিত।
প্রবর্তনীয় পাঠ্যের দ্বারা ইঙ্গিত হিসাবে টেমপ্লেটগুলি বোঝায় এমন নামেরগুলির জন্য একই রকম গোটচা বিদ্যমান।
"টেমপ্লেট" কীওয়ার্ড
উপরের প্রাথমিক উক্তিটি মনে রাখবেন এবং স্ট্যান্ডার্ডকে কীভাবে টেমপ্লেটগুলির জন্য বিশেষ পরিচালনা করার প্রয়োজন রয়েছে? আসুন নিচের নিরীহ-দৃষ্টান্তমূলক উদাহরণটি ধরুন:
boost::function< int() > f;
এটি সম্ভবত একটি মানব পাঠকের কাছে সুস্পষ্ট দেখাচ্ছে। সংকলক জন্য তাই না। নিম্নলিখিত নির্বিচার সংজ্ঞা boost::function
এবং এর কল্পনা করুন f
:
namespace boost { int function = 0; }
int main() {
int f = 0;
boost::function< int() > f;
}
এটি আসলে একটি বৈধ এক্সপ্রেশন ! এটা তোলে কম তুলনা করতে ব্যবহার অপারেটর boost::function
শূন্য (বিরুদ্ধে int()
), এবং তারপর ফলে তুলনা করতে বৃহত্তর-তুলনায় অপারেটর ব্যবহার bool
বিরুদ্ধে f
। তবে আপনি যেমন জানেন যে boost::function
বাস্তব জীবনে একটি টেম্পলেট, তাই সংকলক জানে (14.2 / 3):
নাম অনুসন্ধানের পরে (৩.৪) সন্ধান করে যে কোনও নাম একটি টেম্পলেট-নাম, যদি এই নামটি <টি অনুসরণ করে থাকে তবে <<সর্বদা একটি টেম্পলেট-যুক্তি-তালিকার শুরু হিসাবে নেওয়া হয় এবং নাম হিসাবে অনুসরণ করা হয় না - অপারেটরের চেয়ে।
এখন আমরা আবার একই সমস্যার দিকে ফিরে এসেছি typename
। কোডটি বিশ্লেষণের পরে নামটি কোনও টেম্পলেট কিনা তা আমরা এখনও জানতে না পারলে কী হবে? template
নির্দিষ্ট করা হিসাবে টেম্পলেট নামের আগে আমাদের তাত্ক্ষণিক sert োকানো দরকার 14.2/4
। এটির মতো দেখাচ্ছে:
t::template f<int>(); // call a function template
টেমপ্লেট নাম শুধুমাত্র পরে একটি ঘটতে করতে পারবে না ::
বরং পরে ->
বা .
একটি বর্গ সদস্য অ্যাক্সেস। আপনাকে এখানে কীওয়ার্ডটি সন্নিবেশ করতে হবে:
this->template f<int>(); // call a function template
নির্ভরতা
যে সমস্ত লোকের তাকের উপর ঘন স্ট্যান্ডার্ডিজ বই রয়েছে এবং তারা জানতে চান যে আমি ঠিক কী সম্পর্কে বলছিলাম, আমি কীভাবে এটি স্ট্যান্ডার্ডে নির্দিষ্ট করা হয়েছে সে সম্পর্কে কিছুটা কথা বলব।
টেমপ্লেট ঘোষণায় কিছু কনস্ট্রাক্টরের বিভিন্ন অর্থ রয়েছে যা আপনি টেম্পলেটটি ইনস্ট্যান্ট করতে টেম্পলেট আর্গুমেন্টের উপর নির্ভর করে: এক্সপ্রেশনগুলির বিভিন্ন ধরণের বা মান থাকতে পারে, ভেরিয়েবলের বিভিন্ন ধরণের বা ফাংশন কলগুলি বিভিন্ন ফাংশনগুলি কল করতে পারে। এই জাতীয় নির্মাণগুলি সাধারণত টেম্পলেট পরামিতিগুলির উপর নির্ভর করে বলে ।
স্ট্যান্ডার্ডটি কনস্ট্রাক্ট নির্ভরশীল কিনা তা দ্বারা নিয়মগুলি সুনির্দিষ্টভাবে সংজ্ঞায়িত করে। এটি তাদের যৌক্তিকভাবে বিভিন্ন গোষ্ঠীতে বিভক্ত করে: একটি ধরণের ক্যাচ করে, অন্যটি এক্সপ্রেশনকে ক্যাচ করে। এক্সপ্রেশনগুলি তাদের মান এবং / অথবা তাদের ধরণের উপর নির্ভর করে। তাই আমাদের কাছে রয়েছে আদর্শ উদাহরণ সংযোজন সহ:
- নির্ভরশীল প্রকারগুলি (যেমন: একটি ধরণের টেম্পলেট প্যারামিটার
T
)
- মান-নির্ভর এক্সপ্রেশন (যেমন: একটি নন-টাইপ টেম্পলেট প্যারামিটার
N
)
- টাইপ-নির্ভর এক্সপ্রেশন (যেমন: কোনও টাইপ টেম্পলেট প্যারামিটারে একটি castালাই
(T)0
)
বেশিরভাগ নিয়ম স্বজ্ঞাত এবং পুনরাবৃত্তভাবে নির্মিত হয়: উদাহরণস্বরূপ, কোনও ধরণের T[N]
নির্ভরশীল ধরণের হিসাবে নির্মিত যদি N
মান-নির্ভর অভিব্যক্তি বা T
নির্ভরশীল প্রকার হয়। (14.6.2/1
নির্ভরশীল প্রকারের (14.6.2.2)
জন্য, টাইপ-নির্ভর এক্সপ্রেশন এবং (14.6.2.3)
মান-নির্ভর এক্সপ্রেশনগুলির জন্য বিশদটিতে এর বিশদটি পড়া যেতে পারে ।
নির্ভরশীল নাম
স্ট্যান্ডার্ড কি সম্পর্কে একটু স্পষ্ট নয় ঠিক একটি হল নির্ভরশীল নাম । একটি সাধারণ পাঠে (আপনি জানেন যে, কমপক্ষে অবাক হওয়ার নীতিটি), এটি নির্ভরশীল নাম হিসাবে সংজ্ঞায়িত সমস্ত কিছুই নীচের ফাংশন নামের জন্য বিশেষ ক্ষেত্রে special তবে যেহেতু T::x
ইনস্ট্যান্টেশন প্রসঙ্গে পরিষ্কারভাবে দেখাও দরকার, তাই এটির একটি নির্ভরশীল নামও হওয়া দরকার (ভাগ্যক্রমে, সি সি ++ ১৪ এর মধ্যভাগে কমিটি কীভাবে এই বিভ্রান্তিকর সংজ্ঞাটি ঠিক করতে পারে তা খতিয়ে দেখা শুরু করেছে)।
এই সমস্যাটি এড়াতে, আমি স্ট্যান্ডার্ড পাঠ্যের একটি সহজ ব্যাখ্যার অবলম্বন করেছি। নির্ভরশীল ধরণের বা এক্সপ্রেশন বোঝাতে পারে এমন সমস্ত নির্মাণের মধ্যে একটির একটি উপসেট নাম উপস্থাপন করে। সেই নামগুলি তাই "নির্ভরশীল নাম"। একটি নাম বিভিন্ন রূপ নিতে পারে - স্ট্যান্ডার্ড বলে:
একটি নাম শনাক্তকারী (2.11), অপারেটর-ফাংশন-আইডি (13.5), রূপান্তর-ফাংশন-আইডি (12.3.2), বা টেমপ্লেট-আইডি (14.2) এর ব্যবহার যা কোনও সত্তা বা লেবেল (6.6.4, 6.1)
একটি শনাক্তকারী হ'ল অক্ষর / অঙ্কগুলির একটি সরল অনুক্রম, যখন পরের দুটি হ'ল operator +
এবং operator type
ফর্ম। শেষ ফর্মটি হ'ল template-name <argument list>
। এগুলি সমস্ত নাম, এবং স্ট্যান্ডার্ডের প্রচলিত ব্যবহারের দ্বারা একটি নাম কোয়ালিফায়ারগুলিকে অন্তর্ভুক্ত করতে পারে যা কোন নামের ক্ষেত্র বা শ্রেণীর নাম উল্লেখ করা উচিত say
একটি মান নির্ভর এক্সপ্রেশন 1 + N
কোনও নাম নয়, কিন্তু N
। সমস্ত নির্ভরশীল কনস্ট্রাক্টের উপসেট যে নাম হয় তাকে নির্ভরশীল নাম বলা হয় । ফাংশন নামগুলি, তবে কোনও টেম্পলেটটির বিভিন্ন ইনস্ট্যানটিশনে আলাদা অর্থ হতে পারে, তবে দুর্ভাগ্যক্রমে এই সাধারণ নিয়মে ধরা পড়ে না।
নির্ভরশীল ফাংশন নাম
প্রাথমিকভাবে এই নিবন্ধটির উদ্বেগ নয়, তবে এখনও উল্লেখ করার মতো: ফাংশন নামগুলি একটি ব্যতিক্রম যা পৃথকভাবে পরিচালিত হয়। একটি সনাক্তকারী ফাংশন নাম নিজেই নির্ভর করে না, তবে কলটিতে ব্যবহৃত টাইপ নির্ভর আর্গুমেন্ট এক্সপ্রেশনগুলি দ্বারা নির্ভর করে। উদাহরণস্বরূপ f((T)0)
, f
একটি নির্ভরশীল নাম। স্ট্যান্ডার্ডে এটি নির্দিষ্ট করা আছে (14.6.2/1)
।
অতিরিক্ত নোট এবং উদাহরণ
যথেষ্ট ক্ষেত্রেই আমরা উভয় প্রয়োজন typename
এবং template
। আপনার কোডটি নীচের মতো হওয়া উচিত
template <typename T, typename Tail>
struct UnionNode : public Tail {
// ...
template<typename U> struct inUnion {
typedef typename Tail::template inUnion<U> dummy;
};
// ...
};
মূলশব্দটি template
সর্বদা নামের শেষ অংশে উপস্থিত হয় না। নিম্নলিখিত শ্রেণীর নামের মতো এটি একটি স্কোপ হিসাবে ব্যবহৃত শ্রেণীর নামের আগে এটি মাঝখানে উপস্থিত হতে পারে
typename t::template iterator<int>::value_type v;
কিছু ক্ষেত্রে, কীওয়ার্ডগুলি নিচে বর্ণিত হিসাবে নিষিদ্ধ করা হয়েছে
নির্ভরশীল বেস শ্রেণীর নামে আপনাকে লেখার অনুমতি নেই typename
। ধারণা করা হয় যে প্রদত্ত নামটি একটি শ্রেণির নাম। এটি বেস-শ্রেণীর তালিকার এবং কনস্ট্রাক্টর ইনিশিয়ালাইজার তালিকার উভয় নামের ক্ষেত্রেই সত্য:
template <typename T>
struct derive_from_Has_type : /* typename */ SomeBase<T>::type
{ };
ব্যবহার-ঘোষণায় এটি template
শেষের পরে ব্যবহার করা সম্ভব নয় ::
এবং সি ++ কমিটি কোনও সমাধানে কাজ না করার কথা বলেছিল ।
template <typename T>
struct derive_from_Has_type : SomeBase<T> {
using SomeBase<T>::template type; // error
using typename SomeBase<T>::type; // typename *is* allowed
};