অপারেটর ও ওভারলোড হয়ে গেলে আমি কীভাবে নির্ভরযোগ্যভাবে কোনও অবজেক্টের ঠিকানা পেতে পারি?


170

নিম্নলিখিত প্রোগ্রাম বিবেচনা করুন:

struct ghost
{
    // ghosts like to pretend that they don't exist
    ghost* operator&() const volatile { return 0; }
};

int main()
{
    ghost clyde;
    ghost* clydes_address = &clyde; // darn; that's not clyde's address :'( 
}

আমি কীভাবে clydeতার ঠিকানা পাব ?

আমি এমন একটি সমাধান খুঁজছি যা সমস্ত ধরণের অবজেক্টের জন্য সমানভাবে কাজ করবে। একটি সি ++ 03 সমাধানটি দুর্দান্ত হবে তবে আমি সি ++ 11 সমাধানগুলিতেও আগ্রহী। যদি সম্ভব হয় তবে আসুন কোনও বাস্তবায়ন-নির্দিষ্ট আচরণ এড়ানো যাক।

আমি সি ++ 11 এর std::addressofফাংশন টেম্পলেট সম্পর্কে সচেতন , তবে এটি এখানে ব্যবহার করতে আগ্রহী নই: আমি বুঝতে চাই যে কোনও স্ট্যান্ডার্ড লাইব্রেরি বাস্তবায়নকারী কীভাবে এই ফাংশন টেম্পলেটটি প্রয়োগ করতে পারে।


41
@ জাল্ফ: এই কৌশলটি গ্রহণযোগ্য, তবে এখন আমি মাথা ঘুরিয়ে দিয়ে বলেছি যে ব্যক্তিরা মাথার মধ্যে রয়েছে, আমি তাদের ঘৃণ্য কোডটি কীভাবে কাজ করব? :-)
জেমস ম্যাকনেলিস

5
@ জালফ উহম, কখনও কখনও আপনাকে এই অপারেটরটি ওভারলোড করতে হবে এবং একটি প্রক্সি অবজেক্ট ফিরিয়ে দিতে হবে। যদিও আমি এখনই একটি উদাহরণ চিন্তা করতে পারি না।
কনরাড রুডলফ

5
@ কনরাড: আমাকেও। আপনার যদি এটির প্রয়োজন হয় তবে আমি পরামর্শ দেব যে আপনার ডিজাইনের পুনর্বিবেচনা করার জন্য একটি আরও ভাল বিকল্প হতে পারে, কারণ সেই অপারেটরকে ওভারলোড করা কেবলমাত্র অনেকগুলি সমস্যার কারণ হয়ে দাঁড়ায়। :)
জলফ

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

5
CComPtr<>এবং CComQIPtr<>একটি ওভারলোড হয়েছেoperator&
সাইমন রিখটার

উত্তর:


102

আপডেট: সি ++ 11 এ এর std::addressofপরিবর্তে কেউ ব্যবহার করতে পারে boost::addressof


আসুন প্রথমে বুস্ট থেকে কোডটি অনুলিপি করুন, বিটের চারপাশে সংকলকটির কাজ বিয়োগ:

template<class T>
struct addr_impl_ref
{
  T & v_;

  inline addr_impl_ref( T & v ): v_( v ) {}
  inline operator T& () const { return v_; }

private:
  addr_impl_ref & operator=(const addr_impl_ref &);
};

template<class T>
struct addressof_impl
{
  static inline T * f( T & v, long ) {
    return reinterpret_cast<T*>(
        &const_cast<char&>(reinterpret_cast<const volatile char &>(v)));
  }

  static inline T * f( T * v, int ) { return v; }
};

template<class T>
T * addressof( T & v ) {
  return addressof_impl<T>::f( addr_impl_ref<T>( v ), 0 );
}

আমরা যদি কার্যটির কোনও রেফারেন্স পাস করি তবে কী হবে ?

দ্রষ্টব্য: addressofফাংশনটির জন্য পয়েন্টার সহ ব্যবহার করা যাবে না

সি ++ এ যদি void func();ঘোষিত হয়, funcতবে কোনও ফাংশনটির কোনও প্রসঙ্গ যা কোনও যুক্তি না নিয়ে এবং কোনও ফলাফল না দিয়ে ফিরে আসে। কোনও ক্রিয়াকলাপের এই রেফারেন্সটিকে তুচ্ছভাবে পয়েন্টারে ফাংশনে রূপান্তর করা যায় - থেকে @Konstantin: 13.3.3.2 অনুযায়ী উভয় T &এবং T *ফাংশনগুলির জন্য পৃথক পৃথক। প্রথমটি হ'ল একটি পরিচয় রূপান্তর এবং দ্বিতীয়টি হ'ল ফাংশন-টু-পয়েন্টার রূপান্তর উভয়ই "সঠিক মিল" র‌্যাঙ্ক (১৩.৩.৩.১.১ সারণী 9) পেয়েছেন।

ফাংশন রেফারেন্স মধ্য দিয়ে পাস addr_impl_ref, এর পছন্দ জন্য জমিদার রেজোলিউশনের একটি দ্ব্যর্থতা আছে f, যা মেকি যুক্তি ধন্যবাদ সমাধান করা হয় 0, যা একটি হল intপ্রথম এবং উন্নীত করা যেতে পারে long(সমাকলন রূপান্তর)।

এইভাবে আমরা কেবল পয়েন্টারটি ফিরিয়ে দিই।

যদি আমরা কোনও রূপান্তর অপারেটরের সাথে কোনও প্রকার পাস করি তবে কী হবে?

যদি রূপান্তর অপারেটর একটি ফল দেয় T*তবে আমাদের একটি দ্বিধাগ্রস্ততা থাকে: f(T&,long)দ্বিতীয় আর্গুমেন্টের জন্য ইন্টিগ্রাল প্রচার প্রয়োজন হয়, যখন f(T*,int)রূপান্তর অপারেটরের জন্য প্রথম দিকে ডাকা হয় (@ লিটবকে ধন্যবাদ)

এটি যখন addr_impl_refহবে। দ্য সি ++ স্ট্যান্ডার্ড ম্যান্ডেট কিক করে একটি রূপান্তর ক্রম সর্বাধিক একটি ব্যবহারকারী-সংজ্ঞায়িত রূপান্তর এ থাকতে পারে। প্রকারটি মোড়ানো addr_impl_refএবং ইতিমধ্যে একটি রূপান্তর ক্রম ব্যবহার জোর করে, ধরণের যে কোনও রূপান্তর অপারেটরটি আমরা "অক্ষম" করি।

এইভাবে f(T&,long)ওভারলোডটি নির্বাচিত হয় (এবং ইন্টিগ্রাল প্রচার সম্পাদিত হয়)।

অন্য কোন ধরণের ক্ষেত্রে কী ঘটে?

সুতরাং f(T&,long)ওভারলোডটি নির্বাচিত হয়, কারণ সেখানে টাইপটি T*প্যারামিটারের সাথে মেলে না ।

দ্রষ্টব্য: বোরল্যান্ডের সামঞ্জস্যতা সম্পর্কিত ফাইলের মন্তব্যগুলি থেকে, অ্যারেগুলি পয়েন্টারগুলিতে ক্ষয় হয় না, তবে রেফারেন্স দিয়ে পাস হয়।

এই ওভারলোডে কী ঘটে?

আমরা operator&প্রকারটি প্রয়োগ করা এড়াতে চাই , কারণ এটি অতিরিক্ত লোড হয়েছে।

স্ট্যান্ডার্ড গ্যারান্টি দেয় যা reinterpret_castএই কাজের জন্য ব্যবহার করা যেতে পারে (@ মাত্তিও ইটালিয়া এর উত্তর: 5.2.10 / 10 দেখুন)

সংকলক সতর্কতাগুলি এড়ানোর জন্য বুস্ট কিছু যোগ্যতা constএবং volatileকোয়ালিফায়ার যুক্ত করেছে (এবং সঠিকভাবে const_castসেগুলি সরাতে একটি ব্যবহার করুন)।

  • কাস্ট T&করার জন্যchar const volatile&
  • স্ট্রিপ constএবংvolatile
  • &ঠিকানা নিতে অপারেটর প্রয়োগ করুন
  • ফিরে যাও ক T*

const/ volatileসঙ্গীত এর কালো জাদু একটি বিট, কিন্তু এটা (বরং 4 overloads প্রদানের চেয়ে) কাজ সহজ করে দেয়। নোট যে যেহেতু Tঅযোগ্য, যদি আমরা একটি পাস ghost const&, তারপর T*হয় ghost const*এইভাবে কোয়ালিফায়ার সত্যিই হারিয়ে হয়নি।

সম্পাদনা: পয়েন্টার ওভারলোডটি পয়েন্টার ফাংশনগুলির জন্য ব্যবহৃত হয়, আমি উপরের ব্যাখ্যাটি কিছুটা সংশোধন করেছি। যদিও এখনও এটি প্রয়োজনীয় তা আমি এখনও বুঝতে পারি না ।

নীচের আইডিয়োন আউটপুটটি কিছুটা পরিমাণে এটির যোগফল


2
"আমরা যদি একটি পয়েন্টার পাস করি তবে কী হবে?" অংশটি ভুল। যদি আমরা কোনও প্রকারের U তে কোনও পয়েন্টারটি পাস করি তবে 'T' টাইপটি 'ইউ *' বলে অনুমান করা হয় এবং অ্যাডারে_আইপিএল_রেফের দুটি ওভারলোড থাকে: 'চ (ইউ * এবং দীর্ঘ)' এবং 'চ (ইউ **, int) ', অবশ্যই প্রথমটি নির্বাচিত হবে।
কনস্ট্যান্টিন ওজনোবহিন

@ কনস্টান্টিন: ঠিক আছে, আমি ভেবেছিলাম যে দুটি fওভারলোড যেখানে ফাংশন টেম্পলেটগুলি রয়েছে, যেখানে তারা কোনও টেম্পলেট শ্রেণীর নিয়মিত সদস্য ফাংশন, এটি দেখানোর জন্য ধন্যবাদ। (এখন আমার ওভারলোডের ব্যবহার কী, কোন
টিপসের

এটি একটি দুর্দান্ত, সুস্পষ্টভাবে উত্তর। আমি মূর্তিযুক্ত ধরনের সেখানে একটি বিট এই অপেক্ষা আরও অনেক কিছুতে ছিল "মাধ্যমে ঢালাই char*।" ধন্যবাদ, ম্যাথিউ
জেমস ম্যাকনেলিস

@ জেমস: @ কনস্টান্টিনের কাছ থেকে আমার অনেক বেশি সহায়তা হয়েছে যিনি ভুল করার সময় যে কোনও সময় লাঠি দিয়ে আমার মাথায় আঘাত করবেন: ডি
ম্যাথিউ এম।

3
কেন রূপান্তর ফাংশন রয়েছে এমন ধরণের চারপাশে কাজ করা দরকার? এটি কোনও রূপান্তর ফাংশনটি চাওয়ার চেয়ে সঠিক মিলটি পছন্দ করে না T*? সম্পাদনা: এখন আমি দেখতে। এটি হবে, কিন্তু 0যুক্তি দিয়ে এটি একটি ক্রিস-ক্রসে শেষ হবে , তাই দ্বিধা প্রকাশ হবে।
জোহানেস স্কাউব -

99

ব্যবহার std::addressof

আপনি এটি পর্দার পিছনে নিম্নলিখিত কাজ হিসাবে ভাবতে পারেন:

  1. রেফারেন্স-টু-চর হিসাবে বস্তুর পুনরায় ব্যাখ্যা করুন
  2. এর ঠিকানাটি নিন (ওভারলোডকে কল করবেন না)
  3. আপনার টাইপের একটি পয়েন্টারে ফিরে পয়েন্টারটি কাস্ট করুন।

বিদ্যমান বাস্তবায়নগুলি (বুস্ট.এড্রেসফ সহ) ঠিক এটি করে, কেবলমাত্র অতিরিক্ত যত্ন constএবং volatileযোগ্যতার যত্ন নেওয়া ।


16
আমি এই ব্যাখ্যাটি নির্বাচিতগুলির চেয়ে বেশি পছন্দ করি কারণ এটি সহজেই বোঝা যায়।
টানা স্লেজগাড়ির

49

পিছনে কৌতুক boost::addressofএবং @ লাক ড্যান্টনের প্রদত্ত বাস্তবায়ন এর যাদুতে নির্ভর করে reinterpret_cast; স্ট্যান্ডার্ডটি স্পষ্টভাবে .25.2.10 ¶ 10 এ বর্ণনা করে

"পয়েন্টার টু " টাইপের একটি এক্সপ্রেশন স্পষ্টভাবে একটি ব্যবহার করে "পয়েন্টার টু " টাইপে রূপান্তর করতে পারলে টাইপের একটি লভ্যালু এক্সপ্রেশনটি T1"রেফারেন্স T2" টাইপ T1করতে পারে । এটি হ'ল একটি রেফারেন্স কাস্টটি অন্তর্নির্মিত এবং অপারেটরগুলির সাথে রূপান্তর হিসাবে একই প্রভাব ফেলে । ফলাফলটি এমন একটি লভ্যালু যা উত্স ল্যাভালু হিসাবে একই বস্তুকে বোঝায় তবে ভিন্ন ধরণের।T2reinterpret_castreinterpret_cast<T&>(x)*reinterpret_cast<T*>(&x)&*

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

বুস্ট বাস্তবায়ন সিভি-কোয়ালিফাইড অবজেক্টগুলির সাথে কাজ করার জন্য কয়েকটি পদক্ষেপ যুক্ত করে: প্রথমটি reinterpret_castসম্পন্ন করা হয় const volatile char &, অন্যথায় একটি সরল char &cast ালাই কাজ করে না constএবং / অথবা volatileরেফারেন্সগুলি ( reinterpret_castঅপসারণ করতে পারে না const)। তারপরে constএবং এর volatileসাথে মুছে ফেলা হবে const_cast, ঠিকানাটি সাথে নেওয়া হবে &এবং একটি reinterpet_cast"সঠিক" ধরণের একটি ফাইনাল সম্পন্ন হবে।

const_castমুছে ফেলার জন্য প্রয়োজন হয় const/ volatileঅ-const / উদ্বায়ী রেফারেন্স যোগ করা হয়ে থাকতে পারে, কিন্তু এটা না "ক্ষতি" কি ছিল না const/ volatileকারণ চূড়ান্ত, প্রথম স্থানে রেফারেন্স reinterpret_castইচ্ছা CV-যোগ্যতা পুনরায় যোগ যদি এটা ছিল সেখানে প্রথম স্থানে রয়েছে ( reinterpret_castএটি মুছতে পারে না constতবে এটি যুক্ত করতে পারে)।

কোডের বাকী অংশগুলির জন্য addressof.hpp, দেখে মনে হচ্ছে এটির বেশিরভাগটি কার্যবিধির জন্য। static inline T * f( T * v, int )শুধুমাত্র Borland কম্পাইলার জন্য প্রয়োজন হবে বলে মনে হয়, কিন্তু তার উপস্থিতি প্রয়োজনীয়তার প্রবর্তন addr_impl_ref, অন্যথায় পয়েন্টার ধরনের এই দ্বিতীয় জমিদার হাতে ক্যাচ করা হবে।

সম্পাদনা করুন : বিভিন্ন ওভারলোডের একটি আলাদা ফাংশন রয়েছে, দেখুন @ ম্যাথিউ এম। চমৎকার উত্তর

ঠিক আছে, আমি আর এটি সম্পর্কে নিশ্চিত না; আমার এই কোডটি আরও তদন্ত করা উচিত, তবে এখন আমি রাতের খাবার রান্না করছি :), আমি পরে এটি একবার দেখব।


ম্যাথিউ এম। ঠিকানার পয়েন্টার পাস করার বিষয়ে ব্যাখ্যা ভুল। এই জাতীয় সম্পাদনাগুলি দিয়ে আপনার দুর্দান্ত উত্তরটি ক্ষতিগ্রস্থ করবেন না :)
কনস্টান্টিন ওজনোহিন

"ভাল ক্ষুধা", আরও তদন্তে বোঝা যায় যে কার্যগুলি সম্পর্কে উল্লেখ করার জন্য ওভারলোড ডাকা হয় void func(); boost::addressof(func);। তবে ওভারলোড অপসারণ করা জিসিসি ৪.৩.৪ কোডটি সংকলন করা এবং একই আউটপুট উত্পাদন করা থেকে বিরত রাখে না, তাই কেন এই ওভারলোডটি কেন প্রয়োজনীয় তা আমি এখনও বুঝতে পারি না ।
ম্যাথিউ এম।

@ ম্যাথিউউ: এটি জিসিসিতে একটি বাগ বলে মনে হচ্ছে। ১৩.৩.৩.২ অনুসারে টি এবং টি * উভয়ই ফাংশনগুলির জন্য পৃথকযোগ্য। প্রথমটি হ'ল একটি পরিচয় রূপান্তর এবং দ্বিতীয়টি হ'ল ফাংশন-টু-পয়েন্টার রূপান্তর উভয়ই "সঠিক মিল" র‌্যাঙ্ক (১৩.৩.৩.১.১ সারণী 9) পেয়েছেন। সুতরাং এটি অতিরিক্ত যুক্তি থাকা প্রয়োজন।
কনস্ট্যান্টিন ওজনোবহিন

@ ম্যাথিউউ: স্রেফ এটি জিসিসি ৪.৩.৪ ( আইডিয়োনা / টুফ 34 পি ) দিয়ে চেষ্টা করেছেন এবং প্রত্যাশা অনুযায়ী অস্পষ্টতা পেয়েছেন। আপনি কি বাস্তবায়ন বা বিনামূল্যে ফাংশন টেম্পলেটগুলির মতো ওভারলোড হওয়া সদস্য ফাংশনগুলি চেষ্টা করেছিলেন? দ্বিতীয়টি ( মতাদ্বয়.কম / ভিজেসিআরএসের মতো ) টিমলেট যুক্তি ছাড়ের নিয়মের (14.8.2.1/2) কারণে 'টি *' ওভারলোড চয়ন করবে be
কনস্টান্টিন ওজনোবহিন

2
@ কুরিয়াসগুয়ে: আপনি কেন মনে করেন এটি করা উচিত? আমি নির্দিষ্ট সি ++ স্ট্যান্ডার্ড অংশগুলি উল্লেখ করেছি যা সংকলনকারীদের কী করা উচিত তা উল্লেখ করে এবং সমস্ত সংকলকগুলিতে আমার অ্যাক্সেস রয়েছে (জিসিসি ৪.৩.৪ সহ, তবে সীমাবদ্ধ নয়) আমি যেভাবে বর্ণনা করেছি ঠিক তেমন অস্পষ্টতার প্রতিবেদন করেছি 20 আপনি দয়া করে এই ক্ষেত্রে আপনার যুক্তি বিস্তারিতভাবে বলতে পারেন?
কনস্ট্যান্টিন ওজনোহিন

11

আমি এটি করার একটি বাস্তবায়ন দেখেছি addressof:

char* start = &reinterpret_cast<char&>(clyde);
ghost* pointer_to_clyde = reinterpret_cast<ghost*>(start);

আমাকে জিজ্ঞাসা করবেন না এটি কতটা উপযুক্ত!


5
আইনগত। char*এলিয়াসিং বিধিগুলি টাইপ করার জন্য তালিকাভুক্ত ব্যতিক্রম।
পপি

6
@ ডেড এমএমজি আমি বলছি না এটি মেনে চলছে না। আমি বলছি যে আপনি আমাকে জিজ্ঞাসা করবেন না :)
ড্যান্টন

1
@ ডেড এমএমজি এখানে কোনও এলিয়াসিং সমস্যা নেই। প্রশ্নটি: reinterpret_cast<char*>ভালভাবে সংজ্ঞায়িত।
কৌতূহলী

2
@ কুরিয়াসগুয়ে এবং উত্তর হ্যাঁ, এটি সর্বদা কোনও পয়েন্টার টাইপকে নিক্ষেপ করার অনুমতি দেয় [unsigned] char *এবং এর মাধ্যমে পয়েন্ট-এট অবজেক্টের অবজেক্টের উপস্থাপনাটি পড়তে পারে । এটি এমন আরও একটি ক্ষেত্র যেখানে charবিশেষ সুবিধা রয়েছে।
আন্ডারস্কোর_১

@ আসরকোর্ড_ডি কেবল একটি "ালাই "সর্বদা অনুমোদিত" থাকার অর্থ এই নয় যে আপনি কাস্টের ফলাফল নিয়ে কিছু করতে পারেন।
কৌতূহলী

5

বুস্ট :: ঠিকানা এবং এর বাস্তবায়নের দিকে একবার নজর দিন ।


1
বুস্ট কোডটি আকর্ষণীয় হলেও এর কৌশলটি কীভাবে কাজ করে তা ব্যাখ্যা করে না (বা কেন দুটি ওভারলোডের প্রয়োজন তাও ব্যাখ্যা করে না)।
জেমস ম্যাকনেলিস

আপনি কি 'স্ট্যাটিক ইনলাইন টি * f (টি * ভি, ইনট)' ওভারলোড বোঝাতে চান? দেখে মনে হচ্ছে এটি কেবল বোরল্যান্ড সি কাজের জন্য প্রয়োজন। সেখানে ব্যবহারের পদ্ধতিটি বেশ সোজা। কেবলমাত্র সূক্ষ্ম (অমানুষিক) জিনিসটি 'টি ও' থেকে 'চর ও' তে রূপান্তর। যদিও মানক, 'T *' থেকে 'চর *' এ'ালাইয়ের অনুমতি দেয় সেখানে রেফারেন্স castালাইয়ের জন্য এমন কোনও প্রয়োজনীয়তা নেই বলে মনে হয়। তবুও, কেউ আশা করতে পারে এটি বেশিরভাগ সংকলকগুলিতে ঠিক একই রকম কাজ করবে।
কনস্ট্যান্টিন ওজনোবহিন

@ কনস্টান্টিন: ওভারলোডটি ব্যবহৃত হয় কারণ পয়েন্টারের জন্য, addressofপয়েন্টারটি নিজেই ফেরত দেয়। এটি ব্যবহারকারী যেটি চায় বা না তা তর্কযোগ্য, তবে এটি কীভাবে নির্দিষ্ট করে।
ম্যাথিউ এম।

@ ম্যাথিউ: আপনি কি নিশ্চিত? আমি যতদূর বলতে পারি যে কোনও প্রকার (পয়েন্টার প্রকার সহ) একটি এর ভিতরে আবৃত থাকেaddr_impl_ref , সুতরাং পয়েন্টার ওভারলোড কখনই ডাকা উচিত নয় ...
মাত্তিও ইটালিয়া

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