সি ++ ল্যাম্বা কোড জেনারেশন সি ++ 14 এ ইনিশ ক্যাপচারের সাথে


9

আমি ল্যাবড্ডাসে ক্যাপচারগুলি বিশেষত সি ++ 14 এ যুক্ত হওয়া সাধারণ থিম ক্যাপচারগুলিতে পাস করার পরে উত্পন্ন কোড কোডটি বোঝার / স্পষ্ট করার চেষ্টা করছি।

নীচে তালিকাভুক্ত নিম্নলিখিত কোড নমুনাগুলি দিন এটি সংকলকটি কী উত্পন্ন করবে তা সম্পর্কে আমার বর্তমান বোঝাপড়া।

কেস 1: মান অনুসারে ক্যাপচার / মান অনুসারে ডিফল্ট ক্যাপচার

int x = 6;
auto lambda = [x]() { std::cout << x << std::endl; };

সমান হবে:

class __some_compiler_generated_name {
public:
    __some_compiler_generated_name(int x) : __x{x}{}
    void operator()() const { std::cout << __x << std::endl;}
private:
    int __x;
};

সুতরাং একাধিক অনুলিপি রয়েছে, একটি কনস্ট্রাক্টর প্যারামিটারে অনুলিপি করতে এবং একটিতে সদস্যকে অনুলিপি করতে হবে, যা ভেক্টর ইত্যাদির মতো ধরণের জন্য ব্যয়বহুল হবে would

কেস 2: রেফারেন্স দ্বারা ক্যাপচার / রেফারেন্স দ্বারা ডিফল্ট ক্যাপচার

int x = 6;
auto lambda = [&x]() { std::cout << x << std::endl; };

সমান হবে:

class __some_compiler_generated_name {
public:
    __some_compiler_generated_name(int& x) : x_{x}{}
    void operator()() const { std::cout << x << std::endl;}
private:
    int& x_;
};

প্যারামিটারটি একটি রেফারেন্স এবং সদস্যটি একটি রেফারেন্স তাই কোনও অনুলিপি নেই। ভেক্টর ইত্যাদির মতো দুর্দান্ত

কেস 3:

জেনারেটেড দীক্ষা ক্যাপচার

auto lambda = [x = 33]() { std::cout << x << std::endl; };

আমার আন্ডার স্ট্যান্ডিং এটি মেম্বারটিতে অনুলিপি করা হয়েছে সেই অর্থে কেস 1 এর অনুরূপ।

আমার অনুমান যে সংকলকটি এর মতো কোড উত্পন্ন করে ...

class __some_compiler_generated_name {
public:
    __some_compiler_generated_name() : __x{33}{}
    void operator()() const { std::cout << __x << std::endl;}
private:
    int __x;
};

এছাড়াও আমার যদি নিম্নলিখিতগুলি থাকে:

auto l = [p = std::move(unique_ptr_var)]() {
 // do something with unique_ptr_var
};

কনস্ট্রাক্টর দেখতে কেমন হবে? এটিও কি এটিকে মেম্বারে স্থানান্তরিত করে?


1
@ rafix07 সেক্ষেত্রে উত্পন্ন অন্তর্দৃষ্টি কোডটিও সংকলন করবে না (এটি যুক্তি থেকে অনন্য পিটিআর সদস্যকে অনুলিপি করার চেষ্টা করে)। সিপিপাইনসাইটগুলি সাধারণ বক্তব্য পাওয়ার জন্য দরকারী তবে এটি এখানে এই প্রশ্নের উত্তর দিতে স্পষ্টভাবে সক্ষম নয় able
ম্যাক্স ল্যাঙ্গোফ

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

2
আপনি যদি সি ++ স্ট্যান্ডার্ড সম্পর্কে এটি সম্পর্কে কী বলছেন তাতে আগ্রহী হন, [এক্সফার.প্রিম.লম্বদা] দেখুন । এখানে একটি উত্তর হিসাবে সংক্ষিপ্তসার খুব বেশি।
স্যান্ডার ডি ডিকার

উত্তর:


2

এই প্রশ্নের পুরোপুরি কোডে উত্তর দেওয়া যাবে না। আপনি কিছুটা "সমতুল্য" কোড লিখতে সক্ষম হতে পারেন, তবে স্ট্যান্ডার্ডটি সেভাবে নির্দিষ্ট করা হয়নি।

উপায় যে এর বাইরে, এর মধ্যে ঝাঁপ দাও [expr.prim.lambda]। প্রথম বিষয় লক্ষণীয় যে নির্মাতারা কেবল এতে উল্লেখ করেছেন [expr.prim.lambda.closure]/13:

অবসান একটি সঙ্গে যুক্ত টাইপ ল্যামডা প্রকাশ যদি কোনো ডিফল্ট কনস্ট্রাকটর নেই তা ল্যামডা প্রকাশ টি ল্যামডা-ক্যাপচার এবং একটি খেলাপি ডিফল্ট কন্সট্রাকটর অন্যথায়। এটিতে একটি ডিফল্ট কপির কনস্ট্রাক্টর এবং একটি ডিফল্ট মুভ কনস্ট্রাক্টর ([শ্রেণি.কপি.ক্টোর]) রয়েছে। যদি এটি একটি মোছা কপি নিয়োগ অপারেটর রয়েছে ল্যামডা প্রকাশ টি ল্যামডা-ক্যাপচার এবং খেলাপি কপি এবং পদক্ষেপ নিয়োগ অপারেটার অন্যথায় ([class.copy.assign])। [ দ্রষ্টব্য: এই বিশেষ সদস্য ফাংশনগুলি যথারীতি সুস্পষ্টভাবে সংজ্ঞায়িত করা হয়েছে, এবং তাই মুছে ফেলা হিসাবে সংজ্ঞায়িত করা যেতে পারে। - শেষ নোট ]

সুতরাং ব্যাট থেকে সরাসরি, এটি পরিষ্কার হওয়া উচিত যে কন্সট্রাক্টরগুলি আনুষ্ঠানিকভাবে নয় কীভাবে ক্যাপচারিং অবজেক্টগুলি সংজ্ঞায়িত করা হয়। আপনি বেশ কাছাকাছি যেতে পারেন (সিপিনসাইটস.আইও উত্তর দেখুন), তবে বিশদগুলি পৃথক হয় (নোট 4 এর উত্তরটির কোডটি কীভাবে সংকলন করে না)।


এই কেস 1 টি আলোচনা করার জন্য প্রয়োজনীয় মূল স্ট্যান্ডার্ড ধারাগুলি:

[expr.prim.lambda.capture]/10

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

[expr.prim.lambda.capture]/11

লাম্বডা-এক্সপ্রেশনটির যৌগিক বিবৃতিতে প্রতিটি আইডি-এক্সপ্রেশন যা অনুলিপি দ্বারা বন্দী কোনও সত্তার অদ্ভুত ব্যবহার হয় তা বন্ধের ধরণের সংশ্লিষ্ট নামবিহীন ডেটা সদস্যের অ্যাক্সেসে রূপান্তরিত হয়। [...]

[expr.prim.lambda.capture]/15

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

আপনার কেস 1 এ এটি প্রয়োগ করুন:

কেস 1: মান অনুসারে ক্যাপচার / মান অনুসারে ডিফল্ট ক্যাপচার

int x = 6;
auto lambda = [x]() { std::cout << x << std::endl; };

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

সংক্ষেপে, কেবল একটি অনুলিপি স্থান নেয় । সমাপ্তির ধরণের নির্মাতারা এতে জড়িত নন এবং এটি "সাধারণ" সি ++ এ প্রকাশ করা সম্ভব নয় (নোট করুন যে বন্ধের ধরণটি একটি সামগ্রিক ধরণের নয়)।


রেফারেন্স ক্যাপচার জড়িত [expr.prim.lambda.capture]/12:

কোনও সত্তা রেফারেন্স দ্বারা ক্যাপচার করা হয় যদি তা স্পষ্টভাবে বা স্পষ্টত ক্যাপচার করা হয় তবে অনুলিপি দ্বারা ক্যাপচারিত হয় না। রেফারেন্স দ্বারা বন্দী প্রতিষ্ঠানের জন্য অতিরিক্ত নামবিহীন অ-স্থিতিশীল ডেটা সদস্যদের ক্লোজার টাইপে ঘোষণা করা হয়েছে কিনা তা অনির্দিষ্ট ified [...]

রেফারেন্সগুলির রেফারেন্স ক্যাপচার সম্পর্কে আরও একটি অনুচ্ছেদ রয়েছে তবে আমরা এটি কোথাও করছি না।

সুতরাং, কেস 2 এর জন্য:

কেস 2: রেফারেন্স দ্বারা ক্যাপচার / রেফারেন্স দ্বারা ডিফল্ট ক্যাপচার

int x = 6;
auto lambda = [&x]() { std::cout << x << std::endl; };

কোনও সদস্যকে ক্লোজার টাইপের সাথে যুক্ত করা হয়েছে কিনা তা আমরা জানি না। xল্যাম্বডায় দেহে কেবল সরাসরি xবাইরের দিকে উল্লেখ করা যেতে পারে । এটি নির্ধারণের জন্য সংকলকটির উপর নির্ভর করে এবং এটি মধ্যবর্তী ভাষার কিছু আকারে এটি করবে (যা সংকলক থেকে সংকলক থেকে পৃথক), সি ++ কোডের উত্স রূপান্তর নয়।


ইনিশ ক্যাপচারগুলি এখানে বিস্তারিত [expr.prim.lambda.capture]/6:

একটি দীক্ষা-ক্যাপচারটি এমন আচরণ করে যেমন এটি ঘোষণা করে এবং স্পষ্টভাবে ফর্মটির একটি পরিবর্তনশীল ক্যাপচার করে auto init-capture ;যার ঘোষিত অঞ্চলটি ল্যাম্বডা-এক্সপ্রেশনটির যৌগিক বিবৃতি, ব্যতীত:

  • (.1.১) ক্যাপচারটি যদি অনুলিপি দ্বারা হয় (নীচে দেখুন), ক্যাপচারের জন্য ঘোষিত অ স্থিতিশীল ডেটা সদস্য এবং পরিবর্তনশীল একই পদার্থকে উল্লেখ করার জন্য দুটি ভিন্ন উপায় হিসাবে বিবেচিত হয়, যা অ স্থির তথ্যের আজীবন রয়েছে সদস্য, এবং কোনও অতিরিক্ত অনুলিপি এবং ধ্বংস সঞ্চালিত হয় না, এবং
  • (.2.২) ক্যাপচারটি যদি রেফারেন্স অনুসারে হয় তবে ক্লোজার অবজেক্টের জীবনকাল শেষ হলে ভেরিয়েবলের জীবনকাল শেষ হয়।

দেওয়া হয়েছে, আসুন কেস 3 তাকান:

কেস 3: জড়িত ডিআইইন ক্যাপচার

auto lambda = [x = 33]() { std::cout << x << std::endl; };

যেমনটি বলা হয়েছে, এটিকে একটি পরিবর্তনশীল হিসাবে তৈরি করা হয়েছে auto x = 33;এবং স্পষ্টভাবে অনুলিপি দ্বারা ক্যাপচার করা হয়েছে হিসাবে এটি কল্পনা করুন । এই পরিবর্তনশীল ল্যাম্বডা শরীরের মধ্যে কেবল "দৃশ্যমান"। পূর্বে উল্লিখিত হিসাবে [expr.prim.lambda.capture]/15, __xলম্বা এক্সপ্রেশন মূল্যায়নের পরে প্রদত্ত প্রারম্ভিক দ্বারা ক্লোজার টাইপের ( উত্তরোত্তর জন্য) সম্পর্কিত সদস্যের সূচনা হয়।

সন্দেহ এড়াতে: এর অর্থ এই নয় যে এখানে জিনিসগুলি দু'বার শুরু করা হয়েছে। auto x = 33;একটি "যেন" সরল যেমনটি এর শব্দার্থবিদ্যা উত্তরাধিকারী, এবং বর্ণনা আরম্ভের সেই শব্দার্থবিদ্যা করার একটি পরিবর্তন হয়। কেবল একটি সূচনা ঘটে।

এটি কেস ৪ কেও অন্তর্ভুক্ত করে:

auto l = [p = std::move(unique_ptr_var)]() {
  // do something with unique_ptr_var
};

__p = std::move(unique_ptr_var)ল্যাম্বডা এক্সপ্রেশনটি যখন মূল্যায়ন করা হয় (অর্থাৎ যখন lনির্দিষ্ট করা হয়) তখন ক্লোজার টাইপের সদস্যটি সূচনা করা হয় । থেকে অ্যাকসেস pল্যামডা শরীরে থেকে ব্যবহারের রুপান্তরিত হয় __p


টিএল; ডিআর: কেবলমাত্র সংখ্যক অনুলিপি / প্রারম্ভিককরণ / চালগুলি সম্পাদন করা হয় (যেমনটি আশা / আশা করা যায়)। আমি অনুমান যে lambdas হয় না একটি উৎস রূপান্তর পরিপ্রেক্ষিতে নিদিষ্ট (অন্যান্য অন্বিত চিনি অসদৃশ) ঠিক কারণ কনস্ট্রাকটর পরিপ্রেক্ষিতে কিছু প্রকাশ অতিরিক্ত অপারেশন অবশ্যম্ভাবী হবে।

আমি আশা করি এটি প্রশ্নের মধ্যে প্রকাশিত ভয়কে মীমাংসিত করবে :)


9

কেস 1 [x](){} : উত্পন্ন constনির্মাতা অপ্রয়োজনীয় অনুলিপিগুলি এড়াতে সম্ভবত- যোগ্য রেফারেন্সের মাধ্যমে তার যুক্তিটি গ্রহণ করবে :

__some_compiler_generated_name(const int& x) : x_{x}{}

কেস ২ [x&](){} : আপনার অনুমানগুলি এখানে সঠিক, xরেফারেন্স অনুসারে উত্তীর্ণ এবং সংরক্ষণ করা হয়েছে।


কেস 3 [x = 33](){} : আবার সঠিক, xমান দ্বারা আরম্ভ করা হয়।


কেস ৪ [p = std::move(unique_ptr_var)] : কনস্ট্রাক্টর এর মতো দেখতে পাবেন:

    __some_compiler_generated_name(std::unique_ptr<SomeType>&& x) :
        x_{std::move(x)}{}

হ্যাঁ, বন্ধটি unique_ptr_var"সরানো" হয়েছে। কার্যকর আধুনিক সি ++ এ স্কট মায়ারের আইটেম 32 টিও দেখুন ("অবজেক্টগুলিকে ক্লোজারে স্থানান্তরিত করতে ডিআইআর ক্যাপচার ব্যবহার করুন")।


" constযোগ্য" কেন?
সিপ্লেয়ার্নার

@ ক্লিয়ারিয়নার এমহ, ভাল প্রশ্ন। আমি অনুমান করেছি যে আমি sertedোকিয়েছি কারণ mental মানসিক অটোমেজিজগুলির মধ্যে একটি লাথি মেরেছে ^^ constকিছুটা অস্পষ্টতা / ভাল ম্যাচের কারণে কমপক্ষে এখানে আঘাত করতে পারে না- constইত্যাদি, যাইহোক, আপনি কি ভাবেন যে আমার এই অপসারণ করা উচিত const?
lubgr

আমি মনে করি কনস্ট থাকা উচিত, কি, যদি যুক্তিতে আসলে কনস্ট হয়?
অ্যাকোনকাগুয়া

সুতরাং আপনি বলছেন যে দুটি চাল (বা অনুলিপি) নির্মাণ এখানে ঘটে?
ম্যাক্স ল্যাঙ্গোফ

দুঃখিত, আমি বোঝাতে চাইছি কেস 4 (চলাচলের জন্য) এবং কেস 1 (অনুলিপিগুলির জন্য)। আমার প্রশ্নের অনুলিপি অংশটি আপনার বক্তব্যের উপর ভিত্তি করে কোনও অর্থবোধ করে না (তবে আমি সেই বিবৃতিগুলিতে প্রশ্ন করি)।
ম্যাক্স ল্যাঙ্গোফ

5

সিপিনসাইটস.আইও ব্যবহার করে অনুমান করার দরকার নেই

মামলা 1:
কোড

#include <memory>

int main() {
    int x = 33;
    auto lambda = [x]() { std::cout << x << std::endl; };
}

সংকলক উত্পন্ন

#include <iostream>

int main()
{
  int x = 6;

  class __lambda_5_16
  {
    int x;
    public: 
    inline void operator()() const
    {
      std::cout.operator<<(x).operator<<(std::endl);
    }

    // inline /*constexpr */ __lambda_5_16(const __lambda_5_16 &) = default;
    // inline /*constexpr */ __lambda_5_16(__lambda_5_16 &&) noexcept = default;
    public: __lambda_5_16(int _x)
    : x{_x}
    {}

  };

  __lambda_5_16 lambda = __lambda_5_16(__lambda_5_16{x});
}

কেস 2:
কোড

#include <iostream>
#include <memory>

int main() {
    int x = 33;
    auto lambda = [&x]() { std::cout << x << std::endl; };
}

সংকলক উত্পন্ন

#include <iostream>

int main()
{
  int x = 6;

  class __lambda_5_16
  {
    int & x;
    public: 
    inline void operator()() const
    {
      std::cout.operator<<(x).operator<<(std::endl);
    }

    // inline /*constexpr */ __lambda_5_16(const __lambda_5_16 &) = default;
    // inline /*constexpr */ __lambda_5_16(__lambda_5_16 &&) noexcept = default;
    public: __lambda_5_16(int & _x)
    : x{_x}
    {}

  };

  __lambda_5_16 lambda = __lambda_5_16(__lambda_5_16{x});
}

মামলা 3:
কোড

#include <iostream>

int main() {
    auto lambda = [x = 33]() { std::cout << x << std::endl; };
}

সংকলক উত্পন্ন

#include <iostream>

int main()
{

  class __lambda_4_16
  {
    int x;
    public: 
    inline void operator()() const
    {
      std::cout.operator<<(x).operator<<(std::endl);
    }

    // inline /*constexpr */ __lambda_4_16(const __lambda_4_16 &) = default;
    // inline /*constexpr */ __lambda_4_16(__lambda_4_16 &&) noexcept = default;
    public: __lambda_4_16(int _x)
    : x{_x}
    {}

  };

  __lambda_4_16 lambda = __lambda_4_16(__lambda_4_16{33});
}

কেস 4 (আনুষ্ঠানিকভাবে):
কোড

#include <iostream>
#include <memory>

int main() {
    auto x = std::make_unique<int>(33);
    auto lambda = [x = std::move(x)]() { std::cout << *x << std::endl; };
}

সংকলক উত্পন্ন

// EDITED output to minimize horizontal scrolling
#include <iostream>
#include <memory>

int main()
{
  std::unique_ptr<int, std::default_delete<int> > x = 
      std::unique_ptr<int, std::default_delete<int> >(std::make_unique<int>(33));

  class __lambda_6_16
  {
    std::unique_ptr<int, std::default_delete<int> > x;
    public: 
    inline void operator()() const
    {
      std::cout.operator<<(x.operator*()).operator<<(std::endl);
    }

    // inline __lambda_6_16(const __lambda_6_16 &) = delete;
    // inline __lambda_6_16(__lambda_6_16 &&) noexcept = default;
    public: __lambda_6_16(std::unique_ptr<int, std::default_delete<int> > _x)
    : x{_x}
    {}

  };

  __lambda_6_16 lambda = __lambda_6_16(__lambda_6_16{std::unique_ptr<int, 
                                                     std::default_delete<int> >
                                                         (std::move(x))});
}

এবং আমি বিশ্বাস করি কোডের এই শেষ অংশটি আপনার প্রশ্নের উত্তর দেয়। একটি পদক্ষেপ ঘটে তবে কনস্ট্রাক্টরে [প্রযুক্তিগতভাবে] হয় না।

ক্যাপচারগুলি সেগুলি নয় const, তবে আপনি দেখতে পাবেন যে operator()ফাংশনটি রয়েছে। স্বাভাবিকভাবেই, যদি আপনার ক্যাপচারগুলি সংশোধন করতে হয় তবে আপনি ল্যাম্বডাকে চিহ্নিত করুন mutable


আপনি শেষ কোডটির জন্য যে কোডটি দেখান তা সংকলন করে না। উপসংহারটি "একটি পদক্ষেপ ঘটে তবে কনস্ট্রাক্টরে [প্রযুক্তিগতভাবে নয়" "এই কোডটির দ্বারা সমর্থন করা যায় না।
ম্যাক্স ল্যাঙ্গোফ

কোড কেস 4 অবশ্যই আমার ম্যাক কম্পাইল নেই। আমি অবাক হই যে সিপিনসাইটস থেকে উত্পন্ন বিস্তৃত কোডটি সংকলন করে না। সাইটটি এই মুহুর্তে আমার জন্য বেশ নির্ভরযোগ্য ছিল। আমি তাদের সাথে একটি ইস্যু উত্থাপন করব। সম্পাদনা: আমি নিশ্চিত করেছি যে উত্পন্ন কোডটি সংকলন করে না; এটি এই সম্পাদনা ব্যতীত পরিষ্কার ছিল না।
মধ্যবর্তী

1
আগ্রহের ক্ষেত্রে ইস্যুটির লিঙ্ক: github.com/andreasfertig/cppinsights/issues/258 আমি এখনও SFINAE পরীক্ষা করার মতো বিষয় এবং অন্তর্নিহিত ক্যাসেটগুলি ঘটবে কি না সে জন্য সাইটের জন্য সুপারিশ করছি।
মধ্য
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.