আমাকে কেন এই পয়েন্টারের মাধ্যমে টেমপ্লেট বেস শ্রেণীর সদস্যদের অ্যাক্সেস করতে হবে?


199

তাহলে শ্রেণীর নিচে ছিল না আমি কেবল থাকতে পারে টেমপ্লেট xমধ্যে derivedবর্গ। তবে নীচের কোডটি সহ আমাকে ব্যবহার করতে হবে this->x। কেন?

template <typename T>
class base {

protected:
    int x;
};

template <typename T>
class derived : public base<T> {

public:
    int f() { return this->x; }
};

int main() {
    derived<int> d;
    d.f();
    return 0;
}

1
আহ জিজ্। নাম অনুসন্ধানের সাথে এর কিছু করার আছে। যদি কেউ শীঘ্রই এর উত্তর না দেয় তবে আমি এটি সন্ধান করব এবং এটি পোস্ট করব (এখন ব্যস্ত)।
টেম্পলেটটিফাইফ

@ এড সোয়ানগ্রেন: দুঃখিত, এই প্রশ্নটি পোস্ট করার সময় আমি প্রস্তাবিত উত্তরগুলির মধ্যে এটি মিস করেছি। আমি এর আগে দীর্ঘদিন ধরে উত্তরটি খুঁজছিলাম।
আলী

6
দ্বি-পর্বের নাম দেখার (যা সমস্ত সংকলক ডিফল্টরূপে ব্যবহার করে না) এবং নির্ভরশীল নামগুলির কারণে এটি ঘটে। এই সমস্যার 3 টি সমাধান রয়েছে যার xসাথে উপসর্গ করা ছাড়া this->, যথা: 1) উপসর্গটি ব্যবহার করুনbase<T>::x , 2) একটি বিবৃতি যুক্ত করুন using base<T>::x, 3) একটি বৈশ্বিক সংকলক সুইচ ব্যবহার করুন যা অনুমতি মোড সক্ষম করে। এই সমাধানগুলির পক্ষে মতামতগুলি স্ট্যাকওভারফ্লো
জর্জ রবিনসন

উত্তর:


274

সংক্ষিপ্ত উত্তর: 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কোনও টেম্পলেট না হলেও এটি ছিল একটি সরল ফাংশন।

এখন, স্ট্যান্ডার্ডটি বলে যে নামগুলি টেমপ্লেট প্যারামিটারগুলির উপর নির্ভরশীল নয় তাদের অবশ্যই প্রথম পর্বের মধ্যে সমাধান করতে হবে Ahere এখানে কোনও নির্ভরশীল নাম নয়, এটি নির্বিশেষে একই জিনিসটিকে বোঝায় 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;দ্ব্যর্থক)। আবার, আমি জানি না কীভাবে নিয়মগুলি সম্মত হয়েছিল। আমার অনুমান যে পাঠ্যের যে পৃষ্ঠাগুলির প্রয়োজনীয়তা হবে, প্রচুর নির্দিষ্ট নিয়ম তৈরির বিরুদ্ধে হ্রাস করা হবে যার জন্য প্রসঙ্গটি কোনও প্রকার গ্রহণ করে এবং কোনটি নন-টাইপ।


1
ওহ, সুন্দর বিস্তারিত উত্তর। কয়েকটি জিনিস স্পষ্ট করে বলেছি যে আমি কখনই খোঁজ নিতে বিরক্ত করি নি। :) +1
জাল্ফ

20
@ জালফ: সি ++ কিউটিউবিএফএফএইফএইফএইটিইএনএসইউইউটি কেটিএএএইচএমটিটিবিগো - এর মতো কোনও প্রশ্ন রয়েছে - "আপনি যে উত্তরগুলি জানতে চান এবং আরও গুরুত্বপূর্ণ বিষয়গুলি চালিয়ে যাওয়ার বিষয়ে আপনি নিশ্চিত নন তা ছাড়া আপনি যে প্রশ্নগুলি ঘন ঘন জিজ্ঞাসা করবেন"?
স্টিভ জেসোপ

4
অসাধারণ উত্তর, প্রশ্ন জিজ্ঞাসা ফিট করতে পারে যদি অবাক।
ম্যাথিউ এম।

ওহ, আমরা কি এনসাইক্লোপিডিক বলতে পারি? হাইফাইভ ওয়ান সূক্ষ্ম বিন্দু, যদিও: "যদি ফু কিছু এমন টাইপের সাথে ইনস্ট্যান্ট করা থাকে যা নেস্টেড টাইপ এ এর ​​পরিবর্তে ডেটা সদস্য এ থাকে, তবে তা ইনস্ট্যান্টেশন (ফেজ ২) করার কোডের একটি ত্রুটি, টেমপ্লেটে কোনও ত্রুটি নয় 1)। " বলা ভাল যে টেমপ্লেটটি ত্রুটিযুক্ত নয়, তবে এটি এখনও টেমপ্লেট লেখকের পক্ষ থেকে একটি ভুল অনুমান বা লজিক বাগের ঘটনা হতে পারে। যদি পতাকাঙ্কিত ইনস্ট্যান্টেশনটি আসলে উদ্দেশ্যযুক্ত ইউজকেস ছিল, তবে টেমপ্লেটটি ভুল হবে।
আয়নোক্লাস্ট ব্রিঘাম

1
@JohnH। দেওয়া হয়েছে যে বেশ কয়েকটি সংকলক প্রয়োগ করে -fpermissiveবা অনুরূপ, হ্যাঁ এটি সম্ভব। এটি কীভাবে বাস্তবায়িত হয়েছে তার বিশদ আমি জানি না, তবে xসংকলকটি অবশ্যই প্রকৃত লোভনীয় বর্গ শ্রেণি না জেনে অবধি সমাধানের বিলম্বিত হতে হবে T। সুতরাং, নন-জায়েয মোডে নীতিগতভাবে এটি এটিকে স্থগিত করেছে এমন তথ্য রেকর্ড করতে পারে, এটিকে পিছিয়ে দিতে পারে, একবার অনুসন্ধান করার পরে তা Tকরতে পারে এবং যখন অনুসন্ধানটি আপনার প্রস্তাবিত পাঠ্যটি ইস্যু করে সফল হয়। এটি কেবলমাত্র যেখানে এটি কাজ করে সেই ক্ষেত্রে তৈরি করা হলে এটি একটি খুব সঠিক পরামর্শ হবে: ব্যবহারকারী xঅন্য কোনও সুযোগ থেকে অন্য কিছু বোঝার সম্ভাবনাটি বেশ ক্ষুদ্র!
স্টিভ জেসোপ

13

(জানুয়ারী 10, 2011 থেকে আসল উত্তর)

আমি মনে করি আমি উত্তরটি পেয়েছি: জিসিসি ইস্যু: একটি টেমপ্লেট যুক্তির উপর নির্ভর করে এমন একটি বেস ক্লাসের সদস্য ব্যবহার করে । উত্তরটি সিসির সাথে নির্দিষ্ট নয়।


আপডেট: এম + মাইকেল এর মন্তব্যের জবাবে , সি ++ 11 স্ট্যান্ডার্ডের N3337 খসড়া থেকে :

14.6.2 নির্ভরশীল নাম [টেম্প.ডেপ]
[...]
3 কোনও শ্রেণি বা শ্রেণীর টেমপ্লেটের সংজ্ঞা অনুসারে, যদি একটি বেস শ্রেণি যদি কোনও টেম্পলেট-প্যারামিটারের উপর নির্ভর করে, তবে অযোগ্য নাম অনুসন্ধানের সময় বেস শ্রেণীর স্কোপ পরীক্ষা করা হয় না at শ্রেণীর টেম্পলেট বা সদস্যের সংজ্ঞা বা শ্রেণীর টেম্পলেট বা সদস্যের ইনস্ট্যান্টেশন করার সময়।

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


11

xউত্তরাধিকার সময় লুকানো হয়। আপনি এর মাধ্যমে লুকিয়ে রাখতে পারেন:

template <typename T>
class derived : public base<T> {

public:
    using base<T>::x;             // added "using" statement
    int f() { return x; }
};

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