একটি শ্রেণীর সদস্য ফাংশন টেম্পলেট ভার্চুয়াল হতে পারে?


304

শুনেছি সি ++ শ্রেণীর সদস্য ফাংশন টেম্পলেটগুলি ভার্চুয়াল হতে পারে না। এটা কি সত্য?

যদি সেগুলি ভার্চুয়াল হতে পারে তবে এমন দৃশ্যের উদাহরণ কী যার মধ্যে কেউ এই জাতীয় ফাংশন ব্যবহার করবে?


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

উত্তর:


329

টেমপ্লেটগুলি সমস্ত সংকলন সময়ে সংকলক উত্পাদন কোড সম্পর্কে । ভার্চুয়াল ফাংশন রান-টাইম সিস্টেম figuring আউট যা ফাংশন এ সম্পর্কে কল সম্পর্কে সব হয় রান-টাইম

একবার রান-টাইম সিস্টেমটি বুঝতে পারলে এটি একটি অস্থির ভার্চুয়াল ফাংশন কল করা দরকার, সংকলন সমস্ত সম্পন্ন হয়ে গেছে এবং সংকলকটি আর উপযুক্ত ইনস্ট্যান্স তৈরি করতে পারে না। অতএব আপনার ভার্চুয়াল সদস্য ফাংশন টেম্পলেট থাকতে পারে না।

তবে পলিমারফিজম এবং টেমপ্লেটগুলির সংমিশ্রণে বেশ কয়েকটি শক্তিশালী এবং আকর্ষণীয় কৌশল রয়েছে যা উল্লেখযোগ্যভাবে তথাকথিত ধরণের ক্ষয় হয়


32
আমি এর জন্য কোনও ভাষা কারণ দেখছি না , কেবল বাস্তবায়নের কারণ। vtables ভাষার অংশ নয় - কেবলমাত্র স্ট্যান্ডার্ড পদ্ধতিতে সংকলকরা ভাষাটি প্রয়োগ করে।
gerardw

16
Virtual functions are all about the run-time system figuring out which function to call at run-time- দুঃখিত, তবে এটি এটির একটি ভুল উপায় এবং বেশ বিভ্রান্তিকর। এটি কেবল ইন্দিরেশন, এবং কোনও "রানটাইম ফিগারিং আউট" জড়িত নেই, সংকলনের সময় এটি জানা যায় যে ফাংশনটি ডাকা হবে এটি ভ্যাটেবলের এন-থাই পয়েন্টার দ্বারা চিহ্নিত একটি। "ফিগারিং আউট" বলতে বোঝায় সেখানে টাইপ চেক রয়েছে এবং এরকমটি নয়। Once the run-time system figured out it would need to call a templatized virtual function- ফাংশনটি ভার্চুয়াল কিনা তা সংকলনের সময়টি জানা যায়।
dtech

8
@ ডিড্রাইভার: ১. সংকলকরা যদি এটি দেখে থাকে void f(concr_base& cb, virt_base& vb) { cb.f(); vb.f(); }তবে এটি "জানে" কোন ফাংশনটি cb.f()ডেকে বলা হয় এবং এটি জানেন না vb.f()। আধুনিক খুঁজে পাওয়া যায় নি করা হয়েছে রানটাইম এ , রানটাইম সিস্টেম দ্বারা । আপনি এটিকে "ফিগারিং" বলতে চান এবং এটি আরও কম দক্ষ কিনা, এই তথ্যগুলিকে কিছুটা পরিবর্তন করে না।
এসবিআই

8
@ ডিড্রাইভার: ২. (সদস্য) ফাংশন টেম্পলেটগুলির উদাহরণগুলি (সদস্য) ফাংশন, সুতরাং ভিটিবেলে এই জাতীয় উদাহরণের জন্য কোনও পয়েন্টার রাখার কোনও সমস্যা নেই। তবে কোন টেম্পলেট দৃষ্টান্তগুলির প্রয়োজন তা কেবলমাত্র কলার সংকলন করার সময়ই জানা যায়, যখন বেস ক্লাস এবং উত্পন্ন ক্লাসগুলি সংকলিত করা হয় তখন ভিটিবেলগুলি সেট আপ করা হয়। এবং এগুলি পৃথকভাবে সংকলিত হয়। আরও খারাপ - নতুন উত্পন্ন ক্লাসগুলি রানটাইম চলমান সিস্টেমে লিঙ্ক করা যেতে পারে (মনে করুন আপনার ব্রাউজারটি একটি প্লাগইন গতিশীলভাবে লোড করছে)। এমনকি নতুন কল্পনাযুক্ত শ্রেণি তৈরি করা হলে কলারের উত্স কোডটিও দীর্ঘকাল হারিয়ে যেতে পারে।
এসবিআই

9
@ এসবিআই: আপনি আমার নামের উপর ভিত্তি করে অনুমান করছেন কেন? আমি জেনেরিকস এবং টেমপ্লেটগুলি গুলিয়ে ফেলিনি। আমি জানি যে জাভার জেনেরিকগুলি নির্ভুলভাবে চালানোর সময়। আপনি কেন সি ++ তে ভার্চুয়াল সদস্য ফাংশন টেম্পলেটগুলি রাখতে পারবেন না তা আপনি সম্পূর্ণরূপে ব্যাখ্যা করেন নি, তবে ইনকিউসিটিভ তা করেছে। আপনি 'টাইম' বনাম 'রান টাইম' তৈরির জন্য টেম্পলেট এবং ভার্চুয়াল মেকানিক্সকে অতিমাত্রায় সরল করে দিয়েছেন এবং এই সিদ্ধান্তে পৌঁছেছেন যে "আপনার ভার্চুয়াল সদস্য ফাংশন টেম্পলেট থাকতে পারে না।" আমি ইনকিউসিটিভের উত্তরটি উল্লেখ করেছি, যা "সি ++ টেমপ্লেটস সম্পূর্ণ নির্দেশিকা" উল্লেখ করে। আমি এটিকে "হ্যান্ড-ওয়েভিং" হিসাবে বিবেচনা করি না। আপনার দিনটি শুভ হোক.
জাভানেটর

133

সি ++ টেমপ্লেট থেকে সম্পূর্ণ গাইড:

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


8
আমি মনে করি আজকের সি ++ সংকলক এবং লিংকারগুলি, বিশেষত লিঙ্ক টাইম অপ্টিমাইজেশন সমর্থন সহ, লিংক সময়ে প্রয়োজনীয় ভিটিবেল এবং অফসেট তৈরি করতে সক্ষম হওয়া উচিত। তাহলে সম্ভবত আমরা এই বৈশিষ্ট্যটি সি ++ 2 বি তে পাব?
কাই পেটজকে

33

সি ++ এই মুহুর্তে ভার্চুয়াল টেম্পলেট সদস্য ফাংশনগুলিকে অনুমতি দেয় না। সম্ভবত এটির কারণটি কার্যকর করার জটিলতা। রাজেন্দ্র এখনই এটি করা যায় না কেন তার সঠিক কারণ দেয় তবে মানটির যুক্তিসঙ্গত পরিবর্তনগুলি দিয়ে এটি সম্ভব হতে পারে। ভার্চুয়াল ফাংশন কলের স্থানটি বিবেচনা করে যদি বিশেষত কোনও টেম্প্লেটেড ফাংশনটির কতগুলি ইনস্ট্যান্টেশন প্রকৃতপক্ষে বিদ্যমান থাকে এবং ভিটিবেল তৈরি করা কঠিন মনে হয়। স্ট্যান্ডার্ডের লোকদের কাছে এখনই অন্যান্য অনেক কাজ রয়েছে এবং সি ++ 1x সংকলক লেখকদের জন্যও অনেক কাজ।

আপনি কখন একটি টেম্প্লেটেড সদস্য ফাংশন প্রয়োজন? আমি একবার এমন পরিস্থিতি পেরিয়ে এসেছি যেখানে আমি খাঁটি ভার্চুয়াল বেস ক্লাস সহ একটি শ্রেণিবিন্যাসের রিফ্যাক্টর চেষ্টা করেছি। এটি বিভিন্ন কৌশল বাস্তবায়নের জন্য একটি দুর্বল স্টাইল ছিল। আমি ভার্চুয়াল ফাংশনগুলির একটিটির যুক্তিটিকে একটি সংখ্যাসূচক আকারে পরিবর্তন করতে চেয়েছিলাম এবং সদস্য ফাংশনটি ওভারলোড করার পরিবর্তে এবং ভার্চুয়াল টেমপ্লেট ফাংশনগুলি ব্যবহার করার চেষ্টা করেছি এবং সমস্ত উপ-শ্রেণীর প্রতিটি ওভারলোডকে ওভাররাইড করেছিলাম (এবং তাদের উপস্থিতি নেই তা সন্ধান করতে হয়েছিল) ।)


5
@ পিএমআর: কোড থেকে একটি ভার্চুয়াল ফাংশন কল করা যেতে পারে যা ফাংশন সংকলিত হওয়ার পরেও উপস্থিত ছিল না। সংকলকটি কীভাবে নির্ধারণ করবে যে কোনও (তাত্ত্বিক) ভার্চুয়াল টেম্পলেট সদস্য ফাংশনের কোন উদাহরণ রয়েছে যা এমনকি বিদ্যমান নেই তার জন্য উত্পন্ন করবে?
sbi 9

2
@ এসবিআই: হ্যাঁ, পৃথক সংকলন একটি বিশাল সমস্যা হবে। আমি সি ++ কম্পাইলারগুলিতে মোটেও বিশেষজ্ঞ নই তাই আমি কোনও প্রস্তাব দিতে পারি না। সাধারণভাবে টেম্পলেটড ফাংশনগুলির মতো এটি প্রতিটি সংকলনের ইউনিটে আবার ইনস্ট্যান্ট করা উচিত, তাই না? তাতে কি সমস্যার সমাধান হবে না?
পিএমআর

2
@ এসবিআই যদি আপনি গতিশীলভাবে লাইব্রেরিগুলি লোড করার কথা উল্লেখ করছেন তবে এটি কেবল ভার্চুয়াল টেম্পলেট পদ্ধতি নয়, টেম্পলেট শ্রেণি / ক্রিয়াকলাপগুলির সাথে একটি সাধারণ সমস্যা।
ওক

"সি ++ অনুমতি দেয় না [...]" - মানটির রেফারেন্স দেখার জন্য প্রশংসা করবে (উত্তরটি যখন লেখা হয়েছিল তখন আপ টু ডেট কিনা বা আট বছর পরের একটি আপ টু ডেট নয়) ...
অ্যাকনকাগুয়া

19

ভার্চুয়াল ফাংশন টেবিল

আসুন ভার্চুয়াল ফাংশন টেবিলগুলির কিছু পটভূমি এবং তারা কীভাবে কাজ করে ( উত্স ) দিয়ে শুরু করুন:

[20.3] ভার্চুয়াল এবং নন-ভার্চুয়াল সদস্য ফাংশনগুলি কীভাবে বলা হয় তার মধ্যে পার্থক্য কী?

অ-ভার্চুয়াল সদস্য ফাংশনগুলি স্থিতিশীলভাবে সমাধান করা হয়। অর্থাৎ সদস্য ফাংশনটি বস্তুটির পয়েন্টারের (বা রেফারেন্স) ধরণের ভিত্তিতে স্থিতিশীলভাবে (সংকলন-সময়ে) নির্বাচিত হয়।

বিপরীতে, ভার্চুয়াল সদস্য ফাংশনগুলি গতিশীলভাবে সমাধান করা হয় (রান-টাইমে)। অর্থাৎ সদস্য ফাংশনটি বস্তুর প্রকারের ভিত্তিতে গতিশীলভাবে (রান-টাইমে) নির্বাচিত হয় that বস্তুর পয়েন্টার / রেফারেন্সের ধরণের ভিত্তিতে নয়। একে "গতিশীল বাঁধাই" বলা হয়। বেশিরভাগ সংকলক নিম্নলিখিত কৌশলটির কিছু বৈকল্পিক ব্যবহার করেন: যদি বস্তুর এক বা একাধিক ভার্চুয়াল ফাংশন থাকে তবে সংকলকটি "ভার্চুয়াল-পয়েন্টার" বা "ভি-পয়েন্টার" নামক একটি বস্তুতে একটি গোপন পয়েন্টার রাখে। এই ভি-পয়েন্টারটি "ভার্চুয়াল-টেবিল" বা "ভি-টেবিল" নামক একটি বৈশ্বিক সারণীতে নির্দেশ করে।

সংকলক প্রতিটি শ্রেণীর জন্য একটি ভি-টেবিল তৈরি করে যাতে কমপক্ষে একটি ভার্চুয়াল ফাংশন থাকে। উদাহরণস্বরূপ, যদি শ্রেণি সার্কেলের অঙ্কন () এবং সরানো () এবং আকার পরিবর্তন () এর জন্য ভার্চুয়াল ফাংশন থাকে তবে ক্লাস সার্কেলের সাথে ঠিক একটি ভি-টেবিল যুক্ত থাকবে, এমনকি সেখানে গ্যাজিলিয়ান সার্কেল অবজেক্ট থাকলেও এবং এর ভি-পয়েন্টার ছিল এই সার্কেল অবজেক্টগুলির প্রত্যেকটি সার্কেল ভি-টেবিলের দিকে নির্দেশ করবে। ক্লাসে ভার্চুয়াল ফাংশনগুলির প্রতিটিটিতেই ভি-টেবিলের পয়েন্টার রয়েছে। উদাহরণস্বরূপ, বৃত্ত ভি-টেবিলের তিনটি পয়েন্টার থাকবে: বৃত্তের দিকে একটি পয়েন্টার :: ড্র (), বৃত্তের দিকে একটি পয়েন্টার :: পদক্ষেপ () এবং বৃত্তে একটি পয়েন্টার :: পুনরায় আকার ())

ভার্চুয়াল ফাংশন প্রেরণের সময়, রান-টাইম সিস্টেমটি ক্লাসের ভি-টেবিলের সাথে অবজেক্টের ভি-পয়েন্টারটি অনুসরণ করে, তারপরে ভি-টেবিলে উপযুক্ত কোডটি মেথড কোডে অনুসরণ করে।

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


আমার সমস্যা, বা আমি এখানে কীভাবে এসেছি

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

কিছু কোড:

virtual void  LoadCube(UtpBipCube<float> &Cube,long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void  LoadCube(UtpBipCube<short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void  LoadCube(UtpBipCube<unsigned short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;

আমি এটি হতে চাই তবে এটি ভার্চুয়াল টেম্প্লেটেড কম্বোর কারণে সংকলন করবে না:

template<class T>
    virtual void  LoadCube(UtpBipCube<T> &Cube,long LowerLeftRow=0,long LowerLeftColumn=0,
            long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;

আমি টেমপ্লেট ঘোষণাটি ক্লাস পর্যায়ে নিয়ে এসেছি । এই সমাধান প্রোগ্রামগুলিকে নির্দিষ্ট ধরণের ডেটা পড়ার আগে সেগুলি পড়ার বিষয়ে জানতে বাধ্য করেছিল, যা অগ্রহণযোগ্য।

সমাধান

সতর্কতা, এটি খুব সুন্দর নয় তবে এটি আমাকে পুনরাবৃত্তিমূলক এক্সিকিউশন কোডটি সরিয়ে দেওয়ার অনুমতি দিয়েছে

1) বেস ক্লাসে

virtual void  LoadCube(UtpBipCube<float> &Cube,long LowerLeftRow=0,long LowerLeftColumn=0,
            long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void  LoadCube(UtpBipCube<short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
            long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void  LoadCube(UtpBipCube<unsigned short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
            long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;

2) এবং শিশু ক্লাসে

void  LoadCube(UtpBipCube<float> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1)
{ LoadAnyCube(Cube,LowerLeftRow,LowerLeftColumn,UpperRightRow,UpperRightColumn,LowerBand,UpperBand); }

void  LoadCube(UtpBipCube<short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1)
{ LoadAnyCube(Cube,LowerLeftRow,LowerLeftColumn,UpperRightRow,UpperRightColumn,LowerBand,UpperBand); }

void  LoadCube(UtpBipCube<unsigned short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1)
{ LoadAnyCube(Cube,LowerLeftRow,LowerLeftColumn,UpperRightRow,UpperRightColumn,LowerBand,UpperBand); }

template<class T>
void  LoadAnyCube(UtpBipCube<T> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1);

নোট করুন যে লোডএইনকিউব বেস শ্রেণিতে ঘোষিত নয়।


এখানে কাছাকাছি একটি কাজ সঙ্গে অন্য স্ট্যাক ওভারফ্লো উত্তর আছে: একটি ভার্চুয়াল টেমপ্লেট সদস্য কার্যসংক্রান্ত প্রয়োজন


1
আমি একই অবস্থা, এবং গণশ্রেণীর উত্তরাধিকার কাঠামো পূরণ করেছি। ম্যাক্রোস সাহায্য করেছে।
ZFY

16

নিম্নলিখিত কোডটি উইন্ডো 7 এ MinGW G ++ 3.4.5 ব্যবহার করে সংকলন করে সঠিকভাবে চালিত হতে পারে:

#include <iostream>
#include <string>

using namespace std;

template <typename T>
class A{
public:
    virtual void func1(const T& p)
    {
        cout<<"A:"<<p<<endl;
    }
};

template <typename T>
class B
: public A<T>
{
public:
    virtual void func1(const T& p)
    {
        cout<<"A<--B:"<<p<<endl;
    }
};

int main(int argc, char** argv)
{
    A<string> a;
    B<int> b;
    B<string> c;

    A<string>* p = &a;
    p->func1("A<string> a");
    p = dynamic_cast<A<string>*>(&c);
    p->func1("B<string> c");
    B<int>* q = &b;
    q->func1(3);
}

এবং আউটপুটটি হ'ল:

A:A<string> a
A<--B:B<string> c
A<--B:3

এবং পরে আমি একটি নতুন ক্লাস এক্স যোগ করেছি:

class X
{
public:
    template <typename T>
    virtual void func2(const T& p)
    {
        cout<<"C:"<<p<<endl;
    }
};

আমি যখন দশম শ্রেণিকে প্রধান () এর মতো ব্যবহার করার চেষ্টা করেছি তখন:

X x;
x.func2<string>("X x");

g ++ নিম্নলিখিত ত্রুটিটি রিপোর্ট করুন:

vtempl.cpp:34: error: invalid use of `virtual' in template declaration of `virtu
al void X::func2(const T&)'

সুতরাং এটি সুস্পষ্ট যে:

  • ভার্চুয়াল সদস্য ফাংশন একটি শ্রেণীর টেম্পলেট ব্যবহার করা যেতে পারে। কম্পাইলারের পক্ষে ভেটেবল তৈরি করা সহজ
  • শ্রেণীর টেমপ্লেট সদস্য ফাংশনটিকে ভার্চুয়াল হিসাবে সংজ্ঞায়িত করা অসম্ভব, আপনি দেখতে পাচ্ছেন যে ফাংশনের স্বাক্ষর নির্ধারণ করা এবং ভেটেবল এন্ট্রি বরাদ্দ করা কঠিন।

19
একটি শ্রেণিবদ্ধ টেমপ্লেটে ভার্চুয়াল সদস্যের কার্য থাকতে পারে। সদস্য ফাংশন সদস্য ফাংশন টেম্পলেট এবং ভার্চুয়াল সদস্য ফাংশন উভয়ই হতে পারে।
জেমস ম্যাকনেলিস

1
এটি আসলে জিসিসি ৪.৪.৩ নিয়ে ব্যর্থ হয়। আমার সিস্টেমে নিশ্চিতভাবে উবুন্টু 10.04
ব্লুস্কিন

3
প্রশ্নটি যা চেয়েছিল তা থেকে এটি সম্পূর্ণ আলাদা। এখানে সম্পূর্ণ বেস শ্রেণিভিত্তিক হয়। আমি আগে এই ধরণের জিনিস সংকলন করেছি। এটি ভিজ্যুয়াল স্টুডিও 2010-এও সংকলন করবে
ds-bos-msk

14

না তারা পারে না। কিন্তু:

template<typename T>
class Foo {
public:
  template<typename P>
  void f(const P& p) {
    ((T*)this)->f<P>(p);
  }
};

class Bar : public Foo<Bar> {
public:
  template<typename P>
  void f(const P& p) {
    std::cout << p << std::endl;
  }
};

int main() {
  Bar bar;

  Bar *pbar = &bar;
  pbar -> f(1);

  Foo<Bar> *pfoo = &bar;
  pfoo -> f(1);
};

যদি আপনি যা করতে চান তার একটি সাধারণ ইন্টারফেস থাকে এবং সাবক্লাসে বাস্তবায়ন স্থগিত করে তবে অনেকটা একই প্রভাব রয়েছে।


3
কেউ আগ্রহী হলে এটি সিআরটিপি হিসাবে পরিচিত as
মাইকেল চই

1
তবে এটি কেসগুলিতে সহায়তা করে না, যেখানে কারও শ্রেণি শ্রেণিবিন্যাস রয়েছে এবং বেস ক্লাসগুলিতে পয়েন্টারগুলির ভার্চুয়াল পদ্ধতিগুলি কল করতে সক্ষম হতে চায়। আপনার Fooপয়েন্টারটি যোগ্য হিসাবে যোগ্য Foo<Bar>, এটি কোনও Foo<Barf>বা নির্দেশ করতে পারে না Foo<XXX>
কাই পেটজকে

@ কাইপেটজকে: আপনি একটি নিরবচ্ছিন্ন পয়েন্টার তৈরি করতে পারবেন না, না। তবে আপনি এমন কোনও কোডের টেমপ্লেট করতে পারেন যা কংক্রিটের ধরণের সম্পর্কে জানতে হবে না, যার অনেক একই প্রভাব রয়েছে (ধারণাটি কমপক্ষে - স্পষ্টতই সম্পূর্ণ ভিন্ন বাস্তবায়ন)।
টম

8

না, টেমপ্লেটের সদস্য ফাংশনগুলি ভার্চুয়াল হতে পারে না।


9
আমার কৌতূহল হ'ল: কেন? সংকলক এটি করতে কোন সমস্যার মুখোমুখি হয়?
WannaBeGeek

1
আপনার সুযোগে একটি ঘোষণাপত্রের প্রয়োজন (কমপক্ষে, প্রকারগুলি সঠিক করার জন্য)। আপনার ব্যবহারকারীর শনাক্তকারীদের জন্য সুযোগ (এবং ভাষা) এর একটি সুযোগ থাকা প্রয়োজন।
সজাগভাবে

4

অন্যান্য উত্তরে প্রস্তাবিত টেম্পলেট ফাংশনটি একটি মুখোমুখি এবং কোনও ব্যবহারিক সুবিধা দেয় না।

  • টেমপ্লেট ফাংশন কোড লেখার জন্য কেবল একবার বিভিন্ন ধরণের ব্যবহারের জন্য কার্যকর।
  • ভার্চুয়াল ফাংশনগুলি বিভিন্ন শ্রেণীর জন্য একটি সাধারণ ইন্টারফেস থাকার জন্য দরকারী।

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

তবে প্রতিটি টেম্পলেট ধরণের সংমিশ্রণের জন্য একটি ডামি ভার্চুয়াল মোড়ক ফাংশনটি সংজ্ঞায়িত করা প্রয়োজন:

#include <memory>
#include <iostream>
#include <iomanip>

//---------------------------------------------
// Abstract class with virtual functions
class Geometry {
public:
    virtual void getArea(float &area) = 0;
    virtual void getArea(long double &area) = 0;
};

//---------------------------------------------
// Square
class Square : public Geometry {
public:
    float size {1};

    // virtual wrapper functions call template function for square
    virtual void getArea(float &area) { getAreaT(area); }
    virtual void getArea(long double &area) { getAreaT(area); }

private:
    // Template function for squares
    template <typename T>
    void getAreaT(T &area) {
        area = static_cast<T>(size * size);
    }
};

//---------------------------------------------
// Circle
class Circle : public Geometry  {
public:
    float radius {1};

    // virtual wrapper functions call template function for circle
    virtual void getArea(float &area) { getAreaT(area); }
    virtual void getArea(long double &area) { getAreaT(area); }

private:
    // Template function for Circles
    template <typename T>
    void getAreaT(T &area) {
        area = static_cast<T>(radius * radius * 3.1415926535897932385L);
    }
};


//---------------------------------------------
// Main
int main()
{
    // get area of square using template based function T=float
    std::unique_ptr<Geometry> geometry = std::make_unique<Square>();
    float areaSquare;
    geometry->getArea(areaSquare);

    // get area of circle using template based function T=long double
    geometry = std::make_unique<Circle>();
    long double areaCircle;
    geometry->getArea(areaCircle);

    std::cout << std::setprecision(20) << "Square area is " << areaSquare << ", Circle area is " << areaCircle << std::endl;
    return 0;
}

আউটপুট:

স্কোয়ার আয়তন 1, সার্কেল অঞ্চল 3.1415926535897932385

এখানে চেষ্টা করুন


3

প্রশ্নের দ্বিতীয় অংশের উত্তর দিতে:

যদি সেগুলি ভার্চুয়াল হতে পারে তবে এমন দৃশ্যের উদাহরণ কী যার মধ্যে কেউ এই জাতীয় ফাংশন ব্যবহার করবে?

এটি করতে চাইলে অযৌক্তিক জিনিস নয়। উদাহরণস্বরূপ, জাভা (যেখানে প্রতিটি পদ্ধতি ভার্চুয়াল) জেনেরিক পদ্ধতিতে কোনও সমস্যা নেই।

ভার্চুয়াল ফাংশন টেম্পলেটটি চাওয়ার সি ++ এর একটি উদাহরণ একটি সদস্য ফাংশন যা জেনেরিক পুনরাবৃত্তি গ্রহণ করে। অথবা একটি সদস্য ফাংশন যা জেনেরিক ফাংশন অবজেক্ট গ্রহণ করে।

এই সমস্যার সমাধানটি হল বুস্ট :: যেকোন_আরঞ্জ এবং বুস্ট :: ফাংশন সহ প্রকারের মুছে ফেলা ব্যবহার করা, যা আপনাকে আপনার ফাংশনটিকে একটি টেম্পলেট তৈরি করার প্রয়োজন ছাড়াই জেনেরিক পুনরাবৃত্তকারী বা ফান্টেক্টর গ্রহণ করতে দেয়।


6
জাভা জেনেরিকগুলি কাস্টিংয়ের জন্য সিনট্যাকটিক চিনি। এগুলি টেমপ্লেটগুলির মতো নয়।
ব্রাইস এম। ডেম্পসে

2
@ ব্রাইসএম.ডেম্পসি: আপনি বলতে পারেন যে জাভা জেনেরিকগুলি প্রয়োগ করে, অন্য উপায়ের চেয়ে ... এবং সেমেটিকভাবে, ব্যবহারের ক্ষেত্রে এক্সপ্লিপি উপস্থাপনাগুলি বৈধ আইএমও।
einpoklum

2

'ভার্চুয়াল টেম্পলেট পদ্ধতি' এর জন্য কার্যকারিতা রয়েছে যদি টেম্পলেট পদ্ধতির জন্য টাইপের সেটগুলি আগে থেকেই জানা যায়।

ধারণাটি দেখানোর জন্য, নীচে উদাহরণে কেবলমাত্র দুটি প্রকার ( intএবং double) ব্যবহৃত হয় ।

সেখানে একটি 'ভার্চুয়াল' টেম্পলেট পদ্ধতি ( Base::Method) অনুরূপ ভার্চুয়াল পদ্ধতিটিকে ( Base::VMethodযার মধ্যে একটি) কল করে যা পরিবর্তে টেমপ্লেট পদ্ধতি বাস্তবায়নকে কল করে ( Impl::TMethod)।

একমাত্র TMethodউদ্ভূত বাস্তবায়ন ( AImpl, BImpl) এবং ব্যবহারের ক্ষেত্রে টেমপ্লেট পদ্ধতি প্রয়োগ করতে হবে Derived<*Impl>

class Base
{
public:
    virtual ~Base()
    {
    }

    template <typename T>
    T Method(T t)
    {
        return VMethod(t);
    }

private:
    virtual int VMethod(int t) = 0;
    virtual double VMethod(double t) = 0;
};

template <class Impl>
class Derived : public Impl
{
public:
    template <class... TArgs>
    Derived(TArgs&&... args)
        : Impl(std::forward<TArgs>(args)...)
    {
    }

private:
    int VMethod(int t) final
    {
        return Impl::TMethod(t);
    }

    double VMethod(double t) final
    {
        return Impl::TMethod(t);
    }
};

class AImpl : public Base
{
protected:
    AImpl(int p)
        : i(p)
    {
    }

    template <typename T>
    T TMethod(T t)
    {
        return t - i;
    }

private:
    int i;
};

using A = Derived<AImpl>;

class BImpl : public Base
{
protected:
    BImpl(int p)
        : i(p)
    {
    }

    template <typename T>
    T TMethod(T t)
    {
        return t + i;
    }

private:
    int i;
};

using B = Derived<BImpl>;

int main(int argc, const char* argv[])
{
    A a(1);
    B b(1);
    Base* base = nullptr;

    base = &a;
    std::cout << base->Method(1) << std::endl;
    std::cout << base->Method(2.0) << std::endl;

    base = &b;
    std::cout << base->Method(1) << std::endl;
    std::cout << base->Method(2.0) << std::endl;
}

আউটপুট:

0
1
2
3

এনবি: Base::Methodপ্রকৃত কোডের জন্য আসলে উদ্বৃত্ত ( VMethodজনসাধারণের জন্য সরাসরি ব্যবহার করা যেতে পারে) used আমি এটি যুক্ত করেছি যাতে এটি দেখতে আসল 'ভার্চুয়াল' টেম্পলেট পদ্ধতি হিসাবে দেখায়।


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

আমি ইতিমধ্যে এটিকে কোড অবর্ণন হিসাবে যোগ্য করে তুলেছি এবং আপনি এখনও এই সত্যটি পেতে পারেন না যে আপনি Baseযতক্ষণ না এতক্ষণ প্রয়োগ করা হয়েছে তার সাথে সামঞ্জস্যপূর্ণ নয় এমন আর্গুমেন্ট টাইপের সাথে একটি টেম্পলেট ফাংশন কল করার জন্য আপনাকে প্রত্যেকবার মূল ক্লাসটি সংশোধন করতে হবে। এই প্রয়োজনীয়তা এড়ানো টেম্পলেটগুলির উদ্দেশ্য ...
অ্যাকোনকাগুয়া

এসেলস পদ্ধতির সম্পূর্ণ ভিন্ন: সাধারণ ভার্চুয়াল ফাংশনগুলি বিভিন্ন টেম্পলেট ইনস্ট্যান্টেশনগুলি গ্রহণ করে - এবং উত্পন্ন শ্রেণিতে চূড়ান্ত টেম্পলেট ফাংশনটি কেবল কোড অনুলিপি এড়ানোর জন্য পরিবেশন করে এবং বেস
ক্লাসেও

2

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

// abstract.h

// Simply define the types that each concrete class will use
#define IMPL_RENDER() \
    void render(int a, char *b) override { render_internal<char>(a, b); }   \
    void render(int a, short *b) override { render_internal<short>(a, b); } \
    // ...

class Renderable
{
public:
    // Then, once for each on the abstract
    virtual void render(int a, char *a) = 0;
    virtual void render(int a, short *b) = 0;
    // ...
};

সুতরাং এখন, আমাদের সাবক্লাস বাস্তবায়নের জন্য:

class Box : public Renderable
{
public:
    IMPL_RENDER() // Builds the functions we want

private:
    template<typename T>
    void render_internal(int a, T *b); // One spot for our logic
};

এখানে সুবিধাটি হ'ল, নতুন সমর্থিত প্রকারটি যুক্ত করার সময় এটি সমস্ত বিমূর্ত শিরোলেখ থেকে করা যায় এবং একাধিক উত্স / শিরোনাম ফাইলগুলিতে সম্ভবত এটি সংশোধন করা সম্ভব।


0

কমপক্ষে জিসিসি 5.4 সহ ভার্চুয়াল ফাংশনগুলি টেমপ্লেট সদস্য হতে পারে তবে সেগুলি টেমপ্লেট হতে হবে।

#include <iostream>
#include <string>
class first {
protected:
    virtual std::string  a1() { return "a1"; }
    virtual std::string  mixt() { return a1(); }
};

class last {
protected:
    virtual std::string a2() { return "a2"; }
};

template<class T>  class mix: first , T {
    public:
    virtual std::string mixt() override;
};

template<class T> std::string mix<T>::mixt() {
   return a1()+" before "+T::a2();
}

class mix2: public mix<last>  {
    virtual std::string a1() override { return "mix"; }
};

int main() {
    std::cout << mix2().mixt();
    return 0;
}

আউটপুট

mix before a2
Process finished with exit code 0

0

এটা চেষ্টা কর:

ক্লাসেডার.এ লিখুন:

template <typename T>
class Example{
public:
    T c_value;

    Example(){}

    T Set(T variable)
    {
          return variable;
    }

    virtual Example VirtualFunc(Example paraM)
    {
         return paraM.Set(c_value);
    }

এই কোডটি মেইন কোড পিপিতে লিখতে যদি এটির সাথে কাজ করে তবে পরীক্ষা করুন:

#include <iostream>
#include <classeder.h>

int main()
{
     Example exmpl;
     exmpl.c_value = "Hello, world!";
     std::cout << exmpl.VirtualFunc(exmpl);
     return 0;
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.