আমাকে কোথায় এবং কেন "টেম্পলেট" এবং "টাইপনেম" কীওয়ার্ড রাখতে হবে?


1125

টেম্পলেটগুলিতে, কোথায় এবং কেন আমি দিতে হবে না typenameএবং templateনির্ভরশীল নাম উপর?
যাইহোক নির্ভর নামগুলি কী?

আমার কাছে নিম্নলিখিত কোড রয়েছে:

template <typename T, typename Tail> // Tail will be a UnionNode too.
struct UnionNode : public Tail {
    // ...
    template<typename U> struct inUnion {
        // Q: where to add typename/template here?
        typedef Tail::inUnion<U> dummy; 
    };
    template< > struct inUnion<T> {
    };
};
template <typename T> // For the last node Tn.
struct UnionNode<T, void> {
    // ...
    template<typename U> struct inUnion {
        char fail[ -2 + (sizeof(U)%2) ]; // Cannot be instantiated for any U
    };
    template< > struct inUnion<T> {
    };
};

আমার যে সমস্যা আছে তা typedef Tail::inUnion<U> dummyলাইনে আছে। আমি মোটামুটি নিশ্চিত যে inUnionএটি একটি নির্ভরশীল নাম, এবং ভিসি ++ এটির জন্য দম বন্ধ করার ক্ষেত্রে যথেষ্ট সঠিক।
আমি আরও জানি যে templateসংকলকটি জানাতে আমার কোথাও যোগ করতে সক্ষম হওয়া উচিত যে ইনইউনিয়নটি একটি টেম্পলেট-আইডি। তবে ঠিক কোথায়? এবং এরপরে কি ধরে নেওয়া উচিত যে ইনইয়ন একটি শ্রেণিবদ্ধ টেম্পলেট, অর্থাত inUnion<U>কোনও প্রকারের নাম দেয় এবং একটি ফাংশন নয়?


1
বিরক্তিকর প্রশ্ন: কেন বাড়ানো হচ্ছে না :: বৈকল্পিক?
আসফ লাভি

58
রাজনৈতিক সংবেদনশীলতা, বহনযোগ্যতা।
এমসাল্টারস

5
আমি আপনার আসল প্রশ্নটি ("টেমপ্লেট / টাইপনেম কোথায় রাখব?") চূড়ান্ত প্রশ্ন এবং কোডটি শুরুতে রেখে আরও ভালভাবে দাঁড় করিয়ে দিয়েছিলাম এবং 1024x স্ক্রিনে ফিট করার জন্য কোডটি অনুভূমিকভাবে সংক্ষিপ্ত করে রেখেছি।
জোহানেস স্কাউব -

7
শিরোনাম থেকে "নির্ভরশীল নাম" সরানো হয়েছে কারণ এটি প্রদর্শিত হয় যে বেশিরভাগ লোকেরা যারা "টাইপনাম" এবং "টেম্পলেট" সম্পর্কে অবাক হন তারা "নির্ভরশীল নাম" কী তা জানেন না। এটি তাদের পক্ষে কম বিভ্রান্ত হওয়া উচিত।
জোহানেস স্কাউব -

2
@ এসএমএলটার্স: বুস্ট করা বেশ পোর্টেবল। আমি বলব যে কেবল রাজনীতিই হ'ল সাধারণ কারণেই প্রায়শই বাড়াবাড়ি করা হয়। আমি জানার একমাত্র ভাল কারণটি হ'ল বিল্ড টাইম। অন্যথায় হুইল হুইল রিভেন্টিংয়ে হাজার হাজার ডলার হারাতে হবে।
v.oddou

উত্তর:


1160

( আমার সি ++ 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
     };
    

22
এই উত্তরটি আমার আগের FAQ এন্ট্রি থেকে অনুলিপি করা হয়েছিল যা আমি সরিয়েছি, কারণ আমি খুঁজে পেয়েছি যে কেবলমাত্র উত্তরগুলির উদ্দেশ্যে নতুন "ছদ্ম প্রশ্ন" তৈরির পরিবর্তে আমার বিদ্যমান অনুরূপ প্রশ্নগুলি আরও ভালভাবে ব্যবহার করা উচিত। @ প্রসুনকে ধন্যবাদ , যিনি উত্তরের অংশের (যে ক্ষেত্রে টাইপনাম / টেম্পলেট নিষিদ্ধ) এর ধারণাগুলি সম্পাদনা করেছিলেন।
জোহানেস স্কাউব -

1
আপনি যখন আমাকে এই সিনট্যাক্সটি ব্যবহার করবেন তখন আমাকে সাহায্য করতে পারেন? এই-> টেমপ্লেট f <int> (); আমি এই ত্রুটিটি পেয়েছি 'টেম্পলেট' (একটি বিড়ম্বনা হিসাবে) কেবলমাত্র টেমপ্লেটগুলির মধ্যেই অনুমতি দেওয়া হয় তবে টেমপ্লেট কীওয়ার্ড ব্যতীত এটি ঠিকঠাক কাজ করে।
বলকি

1
আমি আজ একটি অনুরূপ প্রশ্ন জিজ্ঞাসা করেছি, এটি শীঘ্রই সদৃশ হিসাবে চিহ্নিত করা হয়েছিল: stackoverflow.com/questions/27923722/… । আমাকে একটি নতুন প্রশ্ন তৈরির পরিবর্তে এই প্রশ্নটি পুনরুদ্ধার করার জন্য নির্দেশ দেওয়া হয়েছিল। আমাকে অবশ্যই বলতে হবে যে আমি তাদের সদৃশ হওয়ার বিষয়ে একমত নই তবে আমি কে, ঠিক? সুতরাং, typenameসিনট্যাক্স এই সময়ে টাইপ-নাম বাদে অন্য কোনও বিকল্প ব্যাখ্যা দেওয়ার অনুমতি দিলেও কার্যকর হওয়ার কোনও কারণ আছে কি?
জোড়েনহিট

1
@ পাবলো আপনি কিছু মিস করছেন না। তবে এখনও পুরোপুরি লাইনটি অস্পষ্ট না হলে এমনকি বিশৃঙ্খলা লিখতে হবে।
জোহানেস স্কাউব -

1
@ পাবলো এর উদ্দেশ্য হ'ল ভাষা এবং সংকলকগুলি সহজ রাখা। আরও পরিস্থিতি স্বয়ংক্রিয়ভাবে জিনিসগুলি বের করার অনুমতি দেওয়ার প্রস্তাব রয়েছে, যাতে আপনার ঘন ঘন আপনার কীওয়ার্ডের প্রয়োজন হয়। মনে রাখবেন যে আপনার উদাহরণে, টোকেন হয় দ্ব্যর্থক এবং শুধুমাত্র পরে আপনি দেখেছি ">" ডবল পর, আপনি এটি একটি টেমপ্লেট কৌনিক বন্ধনী যেমন দুর্বোধ্য পারবেন না। আরও তথ্যের জন্য, আমি জিজ্ঞাসা করতে ভুল ব্যক্তি, কারণ আমার কাছে একটি সি ++ সংকলক পার্সার প্রয়োগ করার অভিজ্ঞতা নেই।
জোহানেস স্কাউব -

135

সি ++ 11

সমস্যা

আপনার যখন প্রয়োজন হবে typenameএবং templateমূলত যুক্তিসঙ্গত হবেন সে সম্পর্কে C ++ 03 এর বিধিগুলি রয়েছে তবে এর গঠনের একটি বিরক্তিকর অসুবিধা রয়েছে

template<typename T>
struct A {
  typedef int result_type;

  void f() {
    // error, "this" is dependent, "template" keyword needed
    this->g<float>();

    // OK
    g<float>();

    // error, "A<T>" is dependent, "typename" keyword needed
    A<T>::result_type n1;

    // OK
    result_type n2; 
  }

  template<typename U>
  void g();
};

যেমন দেখা যায়, আমাদের ডিসম্পিগিউশন কিওয়ার্ডের প্রয়োজন এমনকি সংকলকটি A::result_typeকেবলমাত্র int(এবং তাই এটি একটি প্রকারের) হতে পারে এবং নিজেরাই পরে ঘোষিত this->gসদস্য টেমপ্লেট হতে পারে g(এমনকি যদি Aস্পষ্টভাবে কোথাও বিশেষভাবে বিশেষায়িত হয় তবে তা হবে) সেই টেম্পলেটটির মধ্যে কোডকে প্রভাবিত করবেন না, সুতরাং এর অর্থ পরবর্তী কোনও বিশেষজ্ঞের দ্বারা প্রভাবিত হতে পারে না A!)।

বর্তমান তাত্পর্য

পরিস্থিতি উন্নত করতে, C ++ 11 এ ভাষার ট্র্যাকগুলি যখন কোনও টাইপটি এনক্লোজিং টেম্পলেটকে বোঝায়। যে জানতে, টাইপ নাম একটি নির্দিষ্ট ফর্ম, যা তার নিজস্ব নাম (উপরে মধ্যে ব্যবহার দ্বারা গঠিত হয়ে থাকতে হবে A, A<T>, ::A<T>)। এই জাতীয় নামের সাথে উল্লেখ করা একটি প্রকার বর্তমান তাত্পর্য হিসাবে পরিচিত । একাধিক প্রকারের উপস্থিতি থাকতে পারে যা বর্তমানের ইনস্ট্যান্টিয়েশন হয় যদি নামটি যে ধরণের নাম থেকে গঠিত হয় তা কোনও সদস্য / নেস্টেড শ্রেণি (তারপরে A::NestedClassএবং Aউভয়ই বর্তমান ইনস্ট্যানটিশন)।

এই ধারণার উপর ভিত্তি করে, ভাষাটি বলে যে CurrentInstantiation::Foo, Fooএবং CurrentInstantiationTyped->Foo(যেমন A *a = this; a->Foo) তারা বর্তমান ইনস্ট্যান্টেশন বা এর একটি নির্ভরশীল বেস শ্রেণীর কোনও শ্রেণির সদস্য হিসাবে পাওয়া গেলে ( এবং কেবলমাত্র করে) সমস্ত বর্তমান ইনস্ট্যান্টের সদস্য হয় নাম তাত্ক্ষণিকভাবে অনুসন্ধান)।

কীওয়ার্ড typenameএবং templateএখন আর প্রয়োজন হয় না যদি কোয়ালিফায়ার বর্তমান ইনস্ট্যান্স একজন সদস্য করা হয়। এখানে একটি keypoint মনে রাখা যে A<T>হয় এখনো (সব পরে একটি টাইপ-নির্ভরশীল নাম Tএছাড়াও নির্ভরশীল টাইপ করা হয়)। তবে A<T>::result_typeএটি একটি প্রকার হিসাবে পরিচিত - সংকলকটি এটিকে নির্ধারণের জন্য "জাদুকরভাবে" এই জাতীয় নির্ভরশীল প্রকারগুলি দেখবে।

struct B {
  typedef int result_type;
};

template<typename T>
struct C { }; // could be specialized!

template<typename T>
struct D : B, C<T> {
  void f() {
    // OK, member of current instantiation!
    // A::result_type is not dependent: int
    D::result_type r1;

    // error, not a member of the current instantiation
    D::questionable_type r2;

    // OK for now - relying on C<T> to provide it
    // But not a member of the current instantiation
    typename D::questionable_type r3;        
  }
};

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

template<>
struct C<int> {
  typedef bool result_type;
  typedef int questionable_type;
};

তাত্ক্ষণিক সময় ত্রুটিটি ধরার জন্য একটি সংকলক প্রয়োজন D<int>::f। সুতরাং আপনি দুটি পৃথিবীর সেরাটি পেয়েছেন: "বিলম্বিত" অনুসন্ধান যদি আপনাকে নির্ভর বেস শ্রেণীর সাথে সমস্যা হতে পারে এবং আপনাকে "অবিলম্বে" অনুসন্ধান যা আপনাকে মুক্তি দেয় typenameএবং থেকে করতে পারে তবে আপনাকে সুরক্ষা দেয় template

অজানা বিশেষায়িতকরণ

কোডে D, নামটি typename D::questionable_typeবর্তমান ইনস্ট্যান্টেশনের সদস্য নয় not পরিবর্তে ভাষা এটি একটি অজানা বিশেষজ্ঞের সদস্য হিসাবে চিহ্নিত করে । বিশেষ করে, সব সময় যখন আপনি করছেন DependentTypeName::Fooবা DependentTypedName->Fooহয় এবং নির্ভরশীল ধরনের না বর্তমান ইনস্ট্যান্স (যে ক্ষেত্রে কম্পাইলার ছেড়ে দিতে হবে এবং "আমরা পরে কি দেখবে পারেন Fooহয়) অথবা এটি হয় বর্তমান ইনস্ট্যান্স এবং নাম এটির বা এটির নির্ভরযোগ্য বেস ক্লাসে পাওয়া যায় নি এবং নির্ভরশীল বেস ক্লাসগুলিও রয়েছে।

hউপরের সংজ্ঞায়িত Aশ্রেণীর টেম্পলেটটির মধ্যে যদি আমাদের সদস্য ফাংশন থাকে তবে কী হবে তা কল্পনা করুন

void h() {
  typename A<T>::questionable_type x;
}

সি ++ তে, ভাষাটি এই ত্রুটিটি ধরার অনুমতি দিয়েছে কারণ তাত্ক্ষণিকভাবে কার্যকর করার কোনও কার্যকর উপায় কখনও থাকতে পারে না A<T>::h(আপনি যে কোনও যুক্তিই দেন T)। সি ++ 11 এ, ভাষাটিতে এখন এই নিয়মটি প্রয়োগ করার জন্য আরও বেশি কারণ দেওয়ার জন্য আরও চেক রয়েছে। যেহেতু Aকোন নির্ভরশীল বেস ক্লাস আছে, এবং Aকোন সদস্য ঘোষণা করে questionable_type, নাম A<T>::questionable_typeহল তন্ন তন্ন বর্তমান ইনস্ট্যান্স সদস্য কিংবাঅজানা বিশেষজ্ঞের একজন সদস্য। সেক্ষেত্রে, কোডটি তাত্ক্ষণিক সময়ে বৈধভাবে সংকলন করতে পারে এমন কোনও উপায় থাকা উচিত না, সুতরাং ভাষাটি এমন একটি নামকে নিষিদ্ধ করে যেখানে যোগ্যতা অর্জনকারী বর্তমান ইনস্ট্যান্টেশন অজ্ঞাত বিশেষায়নের সদস্য বা বর্তমান ইনস্ট্যান্টিশনের সদস্য হতে পারে না (তবে তবে , এই লঙ্ঘন এখনও সনাক্তকরণ প্রয়োজন হয় না)।

উদাহরণ এবং ট্রিভিয়া

আপনি এই উত্তরে এই জ্ঞানটি চেষ্টা করে দেখতে পারেন এবং উপরের সংজ্ঞাগুলি সত্যিকারের বিশ্বের উদাহরণে আপনার জন্য অর্থবোধ করে কিনা (সেগুলি উত্তরটিতে কিছুটা কম বিশদে পুনরাবৃত্তি করা হয়)।

সি ++ 11 বিধিগুলি নিম্নলিখিত বৈধ সি ++ 03 কোডটিকে খারাপভাবে তৈরি করেছে (যা সি ++ কমিটি দ্বারা উদ্দিষ্ট হয়নি, তবে সম্ভবত এটি স্থির করা হবে না)

struct B { void f(); };
struct A : virtual B { void f(); };

template<typename T>
struct C : virtual B, T {
  void g() { this->f(); }
};

int main() { 
  C<A> c; c.g(); 
}

এই বৈধ সি ++ 03 কোড ধারণ করবো this->fকরতে A::fইনস্ট্যান্স সময়ে এবং সবকিছু ঠিক আছে। সি ++ 11 তবে তাত্ক্ষণিকভাবে এটিকে আবদ্ধ করে B::fএবং তাত্ক্ষণিকতার সময় ডাবল-চেক প্রয়োজন, এটি অনুসন্ধান এখনও মিলছে কিনা তা পরীক্ষা করে। তবে তাত্ক্ষণিক সময়ে C<A>::g, আধিপত্য বিধি প্রয়োগ হয় এবং এর A::fপরিবর্তে সন্ধান করা হবে।


fyi - এই উত্তরটি এখানে রেফারেন্স করা হয়েছে: stackoverflow.com/questions/56411114/… এই উত্তরের বেশিরভাগ কোড বিভিন্ন সংকলকগুলিতে সংকলন করে না।
অ্যাডাম রাকিস

@ অ্যাডাম্যাকিস ধরেই নিচ্ছেন যে ২০১৩ সাল থেকে সি ++ স্পেকটি বদল হয়নি (আমি এই উত্তরটি লিখেছিলাম যে তারিখ), তারপরে আপনি যে সংকলকগুলি আপনার কোড দিয়ে চেষ্টা করেছেন সেটি কেবল এই সি ++ 11 + -র বৈশিষ্ট্যটি প্রয়োগ করে না।
জোহানেস স্কাউব -

98

মুখবন্ধ

এই পোস্টটি লিটবের পোস্টের একটি সহজেই পঠনযোগ্য বিকল্প হতে বোঝায়

অন্তর্নিহিত উদ্দেশ্য একই; "কখন?" এবং কেন?" typenameএবং templateঅবশ্যই প্রয়োগ করতে হবে।


এর উদ্দেশ্য কী typenameএবং template?

typenameএবং templateকোনও টেম্পলেট ঘোষণা করার সময় ব্যতীত অন্য পরিস্থিতিতে ব্যবহারযোগ্য able

সি ++ তে কয়েকটি নির্দিষ্ট প্রসঙ্গ রয়েছে যেখানে সংকলককে অবশ্যই কোনও নামের সাথে কীভাবে আচরণ করা যায় তা স্পষ্টভাবে বলতে হবে এবং এই সমস্ত প্রসঙ্গে একটি বিষয় মিল রয়েছে; তারা কমপক্ষে একটি টেম্পলেট-প্যারামিটারের উপর নির্ভর করে ।

আমরা এই জাতীয় নামগুলি উল্লেখ করি, যেখানে ব্যাখ্যায় একটি অস্পষ্টতা থাকতে পারে; " নির্ভরশীল নাম "।

এই পোস্টটি নির্ভরশীল-নাম এবং দুটি কীওয়ার্ডের মধ্যে সম্পর্কের জন্য একটি ব্যাখ্যা সরবরাহ করবে ।


একটি স্নিপপেট 1000 ওয়ার্ডের চেয়ে বেশি বলে

নীচের ফাংশন-টেমপ্লেটে কী চলছে তা বোঝানোর চেষ্টা করুন , নিজের কাছে, বন্ধু বা সম্ভবত আপনার বিড়ালটি; ( ) চিহ্নিত বিবৃতিতে কী হচ্ছে ?

template<class T> void f_tmpl () { T::foo * x; /* <-- (A) */ }


এটি যতটা সহজ মনে হয় তত সহজ নাও হতে পারে, আরও সুনির্দিষ্টভাবে মূল্যায়নের ফলাফল ( ) খুব বেশি নির্ভর করে টেমপ্লেট-প্যারামিটার হিসাবে পাস হওয়া সংজ্ঞাটির উপর T

বিভিন্ন Tএস জড়িত শব্দার্থকে মারাত্মকভাবে পরিবর্তন করতে পারে।

struct X { typedef int       foo;       }; /* (C) --> */ f_tmpl<X> ();
struct Y { static  int const foo = 123; }; /* (D) --> */ f_tmpl<Y> ();


দুটি ভিন্ন পরিস্থিতি :

  • যদি আমরা এক্স ( সি ) হিসাবে টাইপ এক্স দিয়ে ফাংশন-টেম্পলেটটি ইনস্ট্যান্ট করি তবে আমাদের কাছে এক্স নামের একটি পয়েন্টার-টু ইন্টের একটি ঘোষণা থাকবে , তবে;

  • যদি আমরা টেমপ্লেটটি ওয়াই টাইপ দিয়ে ইনস্ট্যান্ট করি , ( ডি ) হিসাবে, ( ) এর পরিবর্তে এমন একটি অভিব্যক্তি থাকে যা ইতিমধ্যে ঘোষিত কিছু ভেরিয়েবল এক্স দিয়ে 123 এর গুণফলের গণনা করে ।



যুক্তিসহ ব্যাখ্যা

সি ++ স্ট্যান্ডার্ড আমাদের নিরাপত্তা এবং মঙ্গল সম্পর্কে কমপক্ষে এই ক্ষেত্রে যত্নশীল care

সম্ভাব্য কদর্য চমকের ভুগছেন থেকে একটি বাস্তবায়ন প্রতিরোধ করার জন্য, স্ট্যান্ডার্ড ম্যান্ডেট আমরা সাজানোর একটি এর অস্পষ্টতা আউট যে নির্ভরশীল-নাম দ্বারা স্পষ্টভাবে অভিপ্রায় যে কোন জায়গায় জানায় আমরা হয় একটি হিসাবে নাম চিকিত্সা চাই টাইপ-নাম , অথবা একটি template- আইডি

যদি কিছুই বর্ণিত না হয় তবে নির্ভরশীল-নামটি হয় হয় একটি ভেরিয়েবল, বা একটি ফাংশন হিসাবে বিবেচিত হবে।



কীভাবে নির্ভরশীল নামগুলি হ্যান্ডেল করবেন ?

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

একটি নির্ভরশীল নাম হ'ল এমন কোনও নাম যা প্রত্যক্ষ বা অপ্রত্যক্ষভাবে কোনও টেম্পলেট-প্যারামিটারের উপর নির্ভর করে ।

template<class T> void g_tmpl () {
   SomeTrait<T>::type                   foo; // (E), ill-formed
   SomeTrait<T>::NestedTrait<int>::type bar; // (F), ill-formed
   foo.data<int> ();                         // (G), ill-formed    
}

উপরের স্নিপেটে আমাদের চারটি নির্ভরশীল নাম রয়েছে:

  • )
    • "প্রকার" নির্ভর করে এর ইনস্ট্যান্টেশনের উপর SomeTrait<T>, যা অন্তর্ভুক্ত করে Tএবং;
  • )
    • "নেস্ট্রেডট্রেট" , যা একটি টেম্পলেট-আইডি নির্ভর করে SomeTrait<T>এবং এর উপর নির্ভর করে ;
    • ( এফ ) এর শেষে "টাইপ" নেস্টেট্রেট উপর নির্ভর করে যা উপর নির্ভর করে SomeTrait<T>এবং;
  • )
    • "তথ্য" , যা একটি মত দেখায় সদস্য ফাংশনের টেমপ্লেট , পরোক্ষভাবে একটি হল নির্ভরশীল-নাম যেহেতু ধরণ foo বিন্যাস এর ইনস্ট্যান্স উপর নির্ভর করে SomeTrait<T>

বিবৃতি ( ), ( এফ ) বা ( জি ) উভয়ই বৈধ নয় যদি সংকলক নির্ভরশীল-নামগুলি ভেরিয়েবল / ফাংশন হিসাবে ব্যাখ্যা করে (যা আগেই বলা হয়েছে যা আমরা স্পষ্টভাবে অন্যথায় না বলি তবে কি ঘটে)।

সমাধান

করতে g_tmplএকটি বৈধ সংজ্ঞা আমরা স্পষ্টভাবে কম্পাইলার বলতে হবে যে আমরা (একটি টাইপ আশা আছে ), একটি টেমপ্লেট-আইডি এবং একটি টাইপ (ইন এফ ), এবং একটি টেমপ্লেট-আইডি (ইন জি )।

template<class T> void g_tmpl () {
   typename SomeTrait<T>::type foo;                            // (G), legal
   typename SomeTrait<T>::template NestedTrait<int>::type bar; // (H), legal
   foo.template data<int> ();                                  // (I), legal
}

প্রত্যেকবার যখন কোনও নাম কোনও প্রকারকে চিহ্নিত করে, তখন জড়িত সমস্ত নাম অবশ্যই টাইপ-নাম বা নেমস্পেসের হতে হবে , এই বিষয়টি মনে রেখে এটি দেখতে সহজ যে আমরা typenameআমাদের পুরোপুরি যোগ্য নামের শুরুতে প্রয়োগ করি ।

templateতবে এ ক্ষেত্রে ভিন্ন, যেহেতু কোনও সিদ্ধান্তে আসার উপায় নেই যেমন; "ওহ, এটি একটি টেম্পলেট, তারপরে এই অন্য জিনিসটিও একটি টেম্পলেট হতে হবে" । এর অর্থ হল যে আমরা যে templateকোনও নামের সাথে আচরণ করতে চাই তার সামনে আমরা সরাসরি আবেদন করি।



আমি কি কোনও নামের সন্ধানে কীওয়ার্ডগুলি আটকে রাখতে পারি?

" আমি কি কেবল কোনও নামের সাথে লেগে থাকতে পারি typenameএবং templateকী প্রসঙ্গে তারা উপস্থিত হয় তা নিয়ে আমি উদ্বিগ্ন হতে চাই না ... " -Some C++ Developer

স্ট্যান্ডার্ডের নিয়মগুলিতে বলা হয়েছে যে আপনি যোগ্য-নাম ( কে ) নিয়ে কাজ করছেন ততক্ষণ আপনি কীওয়ার্ড প্রয়োগ করতে পারেন , তবে নামটি যোগ্য না হলে অ্যাপ্লিকেশনটি দুর্বল ( এল ) হয়।

namespace N {
  template<class T>
  struct X { };
}

         N::         X<int> a; // ...  legal
typename N::template X<int> b; // (K), legal
typename template    X<int> c; // (L), ill-formed

দ্রষ্টব্য : প্রয়োগ typenameবা templateপ্রসঙ্গে যেখানে এটি প্রয়োজন হয় না ভাল অনুশীলন হিসাবে বিবেচিত হয় না; আপনি কিছু করতে পারেন তার অর্থ এই নয় যে আপনার উচিত।


উপরন্তু সেখানে প্রেক্ষিতে কোথায় আছেন typenameএবং templateকরছে স্পষ্টভাবে অননুমোদিত:

  • কোন শ্রেণীর উত্তরাধিকার সূত্রে বেসগুলি নির্দিষ্ট করার সময়

    উদ্ভূত শ্রেণীর বেস-স্পেসিফায়ার-তালিকায় লেখা প্রতিটি নাম ইতিমধ্যে একটি টাইপ-নাম হিসাবে বিবেচিত হয় , স্পষ্টভাবে নির্দিষ্ট করে typenameবলা দুষিত এবং অপ্রয়োজনীয় উভয়ই।

                       // .------- the base-specifier-list
     template<class T> // v
     struct Derived      : typename SomeTrait<T>::type /* <- ill-formed */ {
       ...
     };
    


  • যখন টেমপ্লেট-আইডিটি হ'ল কোনও উদ্ভূত শ্রেণীর ব্যবহারের নির্দেশনা হিসাবে উল্লেখ করা হয়

     struct Base {
       template<class T>
       struct type { };
     };
    
     struct Derived : Base {
       using Base::template type; // ill-formed
       using Base::type;          // legal
     };
    

20
typedef typename Tail::inUnion<U> dummy;

যাইহোক, আমি নিশ্চিত নই যে আপনি ইনইউনিয়ন বাস্তবায়ন করছেন। যদি আমি সঠিকভাবে বুঝতে পারি তবে এই শ্রেণিটি তাত্ক্ষণিকভাবে অনুমিত হওয়ার কথা নয়, সুতরাং "ব্যর্থ" ট্যাবটি কখনই অভ্যাসযোগ্যভাবে ব্যর্থ হয় না। টাইপটি ইউনিয়নে রয়েছে কিনা তা বোঝানো আরও ভাল হবে বা একটি সাধারণ বুলিয়ান মান সহ।

template <typename T, typename TypeList> struct Contains;

template <typename T, typename Head, typename Tail>
struct Contains<T, UnionNode<Head, Tail> >
{
    enum { result = Contains<T, Tail>::result };
};

template <typename T, typename Tail>
struct Contains<T, UnionNode<T, Tail> >
{
    enum { result = true };
};

template <typename T>
struct Contains<T, void>
{
    enum { result = false };
};

পিএস: বুস্ট :: ভেরিয়েন্ট এ একবার দেখুন

পিএস 2: বিশেষত আন্দ্রেই আলেকজান্দ্রেস্কুর বইটিতে টাইপলিস্টগুলি দেখুন : আধুনিক সি ++ ডিজাইন


inUnion <U> তাত্ক্ষণিকভাবে চিহ্নিত করা হবে, আপনি উদাহরণস্বরূপ ইউনিয়ন <ফ্লোট, বুল> :: অপারেটর = (ইউ) সাথে ইউ == ইনট সহ কল ​​করার চেষ্টা করলে। এটি একটি ব্যক্তিগত সেটকে কল করে (ইউ, ইউএনওএন <ইউ> * = 0)।
এমসাল্টারস

এবং ফলাফল = সত্য / মিথ্যা সহ কাজটি হ'ল আমার দরকার হবে :: সক্ষম_আইফ <>, যা আমাদের বর্তমান ওএসএক্স সরঞ্জামচয়ের সাথে সামঞ্জস্যপূর্ণ নয়। যদিও পৃথক টেম্পলেটটি এখনও একটি ভাল ধারণা।
এমসাল্টারস

লুক মানে টাইপিড টেইল :: ইন ইউনিয়ন <ইউ> ডামি; লাইন। এটি লেজকে তাত্ক্ষণিক করে তুলবে। তবে ইউএনওন <ইউ> নয়। এটির পূর্ণ সংজ্ঞা প্রয়োজন হলে এটি তাত্ক্ষণিক হয়। এটি উদাহরণস্বরূপ ঘটে যদি আপনি আকার নিয়ে যান বা কোনও সদস্য অ্যাক্সেস করেন (:: foo ব্যবহার করে)। @ এসএমএল্টাররা যাইহোক, আপনার আর একটি সমস্যা হয়েছে:
জোহানেস স্কাউব - লিটব 5:55

-sizeof (U) কখনই নেতিবাচক নয় :) কারণ আকার_টি একটি স্বাক্ষরবিহীন পূর্ণসংখ্যার প্রকার। আপনি কিছু খুব উচ্চ নম্বর পাবেন। আপনি সম্ভবত আকার (ইউ)> = 1 করতে চান? -1: 1 বা অনুরূপ :)
জোহানেস স্কাউব -

আমি কেবল এটিকে অপরিজ্ঞাত রেখে কেবল এটি ঘোষণা করব: টেমপ্লেট <টাইপনাম ইউ> স্ট্রাক্ট ইন ইউনিয়ন; সুতরাং এটি অবশ্যই তাত্ক্ষণিকভাবে চালু করা যায় না। আমি মনে করি এটি আকারের সাথে রয়েছে, সংকলকটি আপনাকে তাত্ক্ষণিকভাবে না সত্ত্বেও আপনাকে একটি ত্রুটি দেওয়ার অনুমতি দেয় , কারণ যদি জানা থাকে যে আকার (ইউ) সর্বদা> = 1 এবং ...
জোহানেস স্কাউব - লিটব

20

এই উত্তরটি শিরোনামযুক্ত প্রশ্নের উত্তর দেওয়ার (একটি অংশ) বরং একটি সংক্ষিপ্ত এবং মিষ্টি বলে বোঝানো হয়েছে। আপনি যদি সেখানে আরও বিশদ সহ একটি উত্তর চান যা আপনাকে সেগুলি কেন সেখানে রাখতে হবে তা ব্যাখ্যা করে, দয়া করে এখানে যান ।


মূলশব্দটি রাখার সাধারণ নিয়মটি typenameবেশিরভাগ ক্ষেত্রে যখন আপনি কোনও টেম্পলেট প্যারামিটার ব্যবহার করেন এবং আপনি কোনও নেস্টেড typedefবা ব্যবহারকারীর নাম ব্যবহার করতে চান , উদাহরণস্বরূপ:

template<typename T>
struct test {
    using type = T; // no typename required
    using underlying_type = typename T::type // typename required
};

নোট করুন যে এটি মেটা ফাংশন বা এমন জিনিসের ক্ষেত্রেও প্রযোজ্য যা জেনেরিক টেম্পলেট পরামিতিগুলি গ্রহণ করে। তবে, যদি সরবরাহিত টেম্পলেট প্যারামিটারগুলি একটি স্পষ্ট প্রকারের হয় তবে আপনাকে উল্লেখ করতে হবে না typename, উদাহরণস্বরূপ:

template<typename T>
struct test {
    // typename required
    using type = typename std::conditional<true, const T&, T&&>::type;
    // no typename required
    using integer = std::conditional<true, int, float>::type;
};

templateকোয়ালিফায়ার যুক্ত করার সাধারণ নিয়মগুলি বেশিরভাগ ক্ষেত্রে একই রকম থাকে তবে তারা সাধারণত কোনও কাঠামো / শ্রেণীর নিজস্ব টেম্পলেটযুক্ত সদস্য ফাংশন (স্থির বা অন্যথায়) জড়িত থাকে, উদাহরণস্বরূপ:

এই কাঠামো এবং ফাংশন দেওয়া:

template<typename T>
struct test {
    template<typename U>
    void get() const {
        std::cout << "get\n";
    }
};

template<typename T>
void func(const test<T>& t) {
    t.get<int>(); // error
}

t.get<int>()ফাংশনটির ভিতরে থেকে অ্যাক্সেস করার চেষ্টা করার ফলে একটি ত্রুটি ঘটবে:

main.cpp:13:11: error: expected primary-expression before 'int'
     t.get<int>();
           ^
main.cpp:13:11: error: expected ';' before 'int'

সুতরাং এই প্রসঙ্গে আপনার templateআগে কীওয়ার্ডের প্রয়োজন হবে এবং এটিকে এভাবে কল করুন:

t.template get<int>()

এইভাবে সংকলক এর পরিবর্তে এটি সঠিকভাবে পার্স করবে t.get < int


2

আমি সিপিপ্লসপ্লাস ডটকম থেকে অনুরূপ প্রশ্নের ভারব্যাটিমের জন্য জেএলবার্গসের দুর্দান্ত প্রতিক্রিয়া রাখছি, কারণ আমি এই বিষয়টিতে পড়েছি এমন সবচেয়ে সংক্ষিপ্ত ব্যাখ্যা explanation

আমরা যে টেমপ্লেটটিতে লিখি, সেখানে দুটি ধরণের নাম ব্যবহার করা যেতে পারে - নির্ভরশীল নাম এবং অ-নির্ভরশীল নাম। একটি নির্ভরশীল নাম এমন একটি নাম যা কোনও টেম্পলেট প্যারামিটারের উপর নির্ভর করে; টেম্পলেট প্যারামিটারগুলি নির্বিশেষে একটি অ-নির্ভরশীল নামের একই অর্থ রয়েছে।

উদাহরণ স্বরূপ:

template< typename T > void foo( T& x, std::string str, int count )
{
    // these names are looked up during the second phase
    // when foo is instantiated and the type T is known
    x.size(); // dependant name (non-type)
    T::instance_count ; // dependant name (non-type)
    typename T::iterator i ; // dependant name (type)

    // during the first phase, 
    // T::instance_count is treated as a non-type (this is the default)
    // the typename keyword specifies that T::iterator is to be treated as a type.

    // these names are looked up during the first phase
    std::string::size_type s ; // non-dependant name (type)
    std::string::npos ; // non-dependant name (non-type)
    str.empty() ; // non-dependant name (non-type)
    count ; // non-dependant name (non-type)
}

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

প্রথম পর্যায়ে পার্সারকে জানতে হবে যে নির্ভরশীল নাম কোনও প্রকারের নাম বা নন-টাইপের নাম কিনা। ডিফল্টরূপে, নির্ভরশীল নামটি একটি নন-টাইপের নাম বলে ধরে নেওয়া হয়। নির্ভরশীল নামের পূর্বে টাইপনেম কীওয়ার্ডটি উল্লেখ করে যে এটি কোনও প্রকারের নাম।


সারসংক্ষেপ

কেবলমাত্র টেমপ্লেট ঘোষণা এবং সংজ্ঞাতে কীওয়ার্ড টাইপনেমটি ব্যবহার করুন আপনি প্রদত্ত কোনও যোগ্য নাম যা কোনও প্রকারকে বোঝায় এবং কোনও টেম্পলেট প্যারামিটারের উপর নির্ভর করে।

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