এই টেমপ্লেট ফাংশনটি আশানুরূপ আচরণ করে না কেন?


23

আমি টেমপ্লেট ফাংশনগুলি সম্পর্কে পড়ছিলাম এবং এই সমস্যায় বিভ্রান্ত হয়ে পড়েছিলাম:

#include <iostream>

void f(int) {
    std::cout << "f(int)\n";
}

template<typename T>
void g(T val) {
    std::cout << typeid(val).name() << "  ";
    f(val);
}

void f(double) {
    std::cout << "f(double)\n";
}

template void g<double>(double);

int main() {
    f(1.0); // f(double)
    f(1);   // f(int)
    g(1.0); // d  f(int), this is surprising
    g(1);   // i  f(int)
}

আমি না লিখলে ফলাফল একই হয় template void g<double>(double);

আমি মনে করি g<double>পরে instantiated দিতে হবে f(double), সেইজন্য এবং কল fমধ্যে gকল করা উচিত f(double)। আশ্চর্যজনকভাবে, এটি এখনও কল f(int)করে g<double>। কেউ কি আমাকে এটি বুঝতে সাহায্য করতে পারে?


উত্তরগুলি পড়ার পরে, আমি বুঝতে পারি যে আমার বিভ্রান্তি আসলে কী।

এখানে একটি আপডেট উদাহরণ। এটির জন্য আমি একটি বিশেষীকরণ যুক্ত করেছি তা ছাড়া এটি বেশিরভাগ অপরিবর্তিত রয়েছে g<double>:

#include <iostream>

void f(int){cout << "f(int)" << endl;}

template<typename T>
void g(T val)
{
    cout << typeid(val).name() << "  ";
    f(val);
}

void f(double){cout << "f(double)" << endl;}

//Now use user specialization to replace
//template void g<double>(double);

template<>
void g<double>(double val)
{
    cout << typeid(val).name() << "  ";
    f(val);
}

int main() {
    f(1.0); // f(double)
    f(1);  // f(int)
    g(1.0); // now d  f(double)
    g(1);  // i  f(int)
}

ব্যবহারকারী বিশেষায়নের সাথে, g(1.0)আমার প্রত্যাশা অনুযায়ী আচরণ করে।

সংকলকটি স্বয়ংক্রিয়ভাবে g<double>একই জায়গায় (একই কি সি ++ প্রোগ্রামিং ল্যাঙ্গুয়েজ , চতুর্থ সংস্করণের ২ main()26.৩.৩ অনুচ্ছেদে বর্ণিত হিসাবে একই ইনস্ট্যান্টেশনটি করা উচিত নয় )?


3
শেষ কল, আমার জন্য g(1)দেয় i f(int)। আপনি লিখেছেন d f(double)। এটি কি টাইপো ছিল?
এইচটিএনডাব্লু

হ্যাঁ. দুঃখিত। আপডেট হয়েছে
ঝংকি চেং

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

উত্তর:


12

নামটি fএকটি নির্ভরশীল নাম (এটি Tতর্কের মাধ্যমে নির্ভর করে val) এবং এটি দুটি ধাপে সমাধান করা হবে :

  1. নন-এডিএল লুকআপ ফাংশন ঘোষণাগুলি পরীক্ষা করে ... যা টেম্পলেট সংজ্ঞা প্রসঙ্গ থেকে দৃশ্যমান ।
  2. এডিএল ফাংশন ঘোষণাগুলি পরীক্ষা করে ... যা টেম্পলেট সংজ্ঞা প্রসঙ্গে বা টেমপ্লেট ইনস্ট্যান্টেশন প্রসঙ্গ থেকে দৃশ্যমান ।

void f(double)টেম্পলেট সংজ্ঞা প্রসঙ্গ থেকে দৃশ্যমান নয়, এবং এডিএল এটিও খুঁজে পাবে না, কারণ

মৌলিক ধরণের আর্গুমেন্টের জন্য, নেমস্পেস এবং ক্লাসের সম্পর্কিত সেটটি খালি


আমরা আপনার উদাহরণটি কিছুটা সংশোধন করতে পারি:

struct Int {};
struct Double : Int {};

void f(Int) { 
    std::cout << "f(Int)";
}

template<typename T>
void g(T val) {
    std::cout << typeid(val).name() << ' ';
    f(val);
    // (f)(val);
}

void f(Double) { 
    std::cout << "f(Double)";
}

int main() {
    g(Double{});
}

এখন এডিএল void f(Double)দ্বিতীয় ধাপে খুঁজে পাবেন , এবং আউটপুট হবে 6Double f(Double)। আমরা পরিবর্তে (f)(val)(বা ::f(val)) লিখে ADL অক্ষম করতে পারি f(val)। তারপরে আউটপুটটি 6Double f(Int)আপনার উদাহরণের সাথে একমত হবে ।


আপনাকে অনেক ধন্যবাদ. আমি ভাবছি যে কোডটিতে জি <ডাবল> এর ইনস্ট্যান্টেশনটি কোথায়। এটি কি ঠিক আগে ()। যদি তা হয় তবে তাত্ক্ষণিক g <ডাবল> সংজ্ঞাটি উভয় এফ (ইন্ট) এবং এফ (ডাবল) দেখতে এবং শেষ পর্যন্ত চ (ডাবল) বাছাই করতে সক্ষম হবে না?
ঝংকি চেং

@ ঝংকিচেইং প্রথম ধাপে কেবলমাত্র টেমপ্লেট সংজ্ঞা প্রসঙ্গে বিবেচনা করা হবে এবং সেই প্রেক্ষাপট থেকে void f(double)দৃশ্যমান নয় - এই প্রসঙ্গটি তার ঘোষণার আগে শেষ হয়। পদক্ষেপ 2 এডিএল কিছু পাবেন না, সুতরাং টেমপ্লেট ইনস্ট্যান্টেশন প্রসঙ্গ এখানে কোন ভূমিকা পালন করে না।
এভগ

@ ঝংগি চেং, আপনার সম্পাদনায় আপনি পরে একটি সংজ্ঞা চালু করেছিলেন void f(double), সুতরাং এটি থেকে এটি ফাংশনটি দৃশ্যমান। এখন fকোনও নির্ভরশীল নাম নয়। f(val);সংজ্ঞার পরে ঘোষিত করার জন্য যদি আরও ভাল ম্যাচ হয় তবে g<double>এটিও পাওয়া যাবে না। "সামনের দিকে তাকানোর" একমাত্র উপায় হ'ল এডিএল (বা কিছু পুরাতন সংকলক যা দ্বি-পর্যায়ের চেহারা সঠিকভাবে প্রয়োগ করে না)।
এভগ

আপনার উত্তর সম্পর্কে আমার বোঝা এখানে। আমার ধারণা করা উচিত যে ফাংশন টেম্পলেটগুলি (g <int> এবং g <ডাবল>) টেমপ্লেট সংজ্ঞার ঠিক পরে ইনস্ট্যান্ট হয়। সুতরাং এটি f (ডাবল) দেখতে পাবেন না। এটা কি সঠিক. তোমাকে অনেক ধন্যবাদ.
ঝংকি চেং

@ ঝংকিচেইং, ঠিক আগে তড়িঘড়ি করা হয়েছে main()। তারা দেখতে পাবে না f(double), কারণ যখন তাত্ক্ষণিক ঘটনা ঘটে তখন খুব দেরি হয়ে যায়: প্রথম পর্যবেক্ষণের কাজটি ইতিমধ্যে হয়ে গিয়েছে এবং এটির সন্ধান পাওয়া যায় নি f(double)
এভগ

6

f(double)আপনি যে স্থানে ডাকেন সেখানে সমস্যাটি ঘোষিত হয়নি; যদি আপনি এর ঘোষণাটি সামনে রাখেন template g, এটি কল হবে।

সম্পাদনা করুন: কেন কেউ ম্যানুয়াল ইনস্ট্যান্টেশন ব্যবহার করবে?

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

সি ++ প্রোগ্রামটি দুটি ধাপে বাইনারিগুলিতে তৈরি করা হয়েছে: সংকলন এবং লিঙ্কিং। কোনও ফাংশন কল সংকলনের জন্য শুধুমাত্র ফাংশনটির শিরোনামই সফল হয়। লিঙ্কিং সফল হওয়ার জন্য, ফাংশনের সংকলিত বডিযুক্ত একটি অবজেক্ট ফাইল প্রয়োজন।

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

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

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

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

এটা কি কোন চেতনা তৈরী করে?


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

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

আমার প্রশ্নটি কোডের সেই অংশটি অবিকল: "টেমপ্লেটটি শূন্য; <ডাবল> (ডাবল);" প্রোগ্রামিং টেম্পলেটে এটির নামকরণ করা হয়েছে, যদি আপনি তা জানেন। বিশেষীকরণটি কিছুটা আলাদা, কারণ এটি দ্বিতীয় কোডের মতো ঘোষণা করা হয়েছে যে লেখক "টেমপ্লেট <> শূন্য জি <ডাবল> (ডাবল ভ্যাল) {কোট << টাইপিড (ভাল)। নাম () <<" "; চ (প্রেরণ ভাল);} "আপনি আমাকে পার্থক্য ব্যাখ্যা করতে পারেন?
দেব

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

1
template void g<double>(double);তাই ম্যানুয়াল ইনস্ট্যান্স (নোট বলা হয় templateকোন কোণ বন্ধনী সঙ্গে, বাক্য গঠন একটি পার্থক্য বৈশিষ্ট্য যে); এটি পদ্ধতির একটি উদাহরণ তৈরি করতে সংকলককে বলে। এখানে এটির খুব কম প্রভাব রয়েছে, যদি এটি না থাকে তবে সংকলকটি যে জায়গাতে উদাহরণটি বলা হয় সেখানে উদাহরণটি তৈরি করে। ম্যানুয়াল ইনস্ট্যান্টেশনটি খুব কমই বৈশিষ্ট্য ব্যবহৃত হয়, আমি বলব যে বিষয়টি পরিষ্কার হওয়ার পরে আপনি কেন এটি ব্যবহার করতে চাইতে পারেন :-)
অ্যাশলেউইলকস
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.