সি ++ 11 এর ব্যাপ্তি ভিত্তিক ব্যবহারের সঠিক উপায় কী?


211

সি ++ 11 এর পরিসীমা ভিত্তিক ব্যবহারের সঠিক উপায় কী for?

কোন সিনট্যাক্স ব্যবহার করা উচিত? for (auto elem : container), বা for (auto& elem : container)বা for (const auto& elem : container)? নাকি অন্য কিছু?


6
একই বিবেচনা ফাংশন আর্গুমেন্ট হিসাবে প্রযোজ্য।
ম্যাক্সিম এগুরুশকিন

3
প্রকৃতপক্ষে, এর জন্য পরিসীমা ভিত্তিক সাথে সামান্যই সম্পর্ক রয়েছে। একই বিষয়ে যে কোনও সম্পর্কে বলা যেতে পারে auto (const)(&) x = <expr>;
ম্যাথিউ এম।

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

2
@ মিঃ সি 6464: যতদূর আমি উদ্বিগ্ন, autoসাধারণভাবে এর সাথে পরিসীমা ভিত্তিক না হয়ে আরও কিছু করার আছে ; আপনি নিখুঁত বিনা জন্য পরিসীমা ভিত্তিক ব্যবহার করতে পারেন auto! for (int i: v) {}পুরোপুরি ঠিক আছে। অবশ্যই, আপনি আপনার উত্তরে যে পয়েন্টগুলি উত্থাপন করছেন তার বেশিরভাগের সাথে প্রকারের সাথে আরও বেশি সম্পর্ক থাকতে পারে auto... তবে প্রশ্নটি থেকে ব্যথা পয়েন্টটি কোথায় তা পরিষ্কার নয়। ব্যক্তিগতভাবে, আমি autoপ্রশ্ন থেকে সরিয়ে ফেলার চেষ্টা করব ; বা সম্ভবত এটি স্পষ্ট করে তুলুন যে আপনি autoপ্রকারটি ব্যবহার করেন বা স্পষ্টতই নামকরণ করেন না কেন, প্রশ্নটি মান / রেফারেন্সের উপর কেন্দ্রীভূত।
ম্যাথিউ এম।

1
@ ম্যাথিউইউম: আমি শিরোনাম পরিবর্তন করতে বা প্রশ্নটি কোনও আকারে সম্পাদনা করতে উন্মুক্ত যা তাদের আরও স্পষ্ট করে তুলতে পারে ... আবার, আমার ফোকাসটি সিনট্যাক্সের জন্য পরিসর-ভিত্তিক কয়েকটি বিকল্প নিয়ে আলোচনা করা ছিল (কোডটি সংকলন করে যা সংকলন করছে তবে অদক্ষ, কোড যা সংকলন করতে ব্যর্থ হয় ইত্যাদি) এবং লুপগুলির জন্য সি ++ 11 পরিসীমা ভিত্তিক কারও কাছে কিছু নির্দেশনা দেওয়ার (বিশেষত শিক্ষানবিশ স্তরে) কিছু নির্দেশ দেওয়ার চেষ্টা করে।
মিঃসি 64

উত্তর:


389

এর মধ্যে পার্থক্য শুরু করা যাক দেখে বনাম কন্টেইনারে উপাদান পরিবর্তন তাদের জায়গায়।

উপাদান পর্যবেক্ষণ

আসুন একটি সহজ উদাহরণ বিবেচনা করুন:

vector<int> v = {1, 3, 5, 7, 9};

for (auto x : v)
    cout << x << ' ';

উপরের কোডটিতে উপাদানগুলি intগুলি প্রিন্ট করে vector:

1 3 5 7 9

এখন অন্য একটি ক্ষেত্রে বিবেচনা করুন, যাতে ভেক্টর উপাদানগুলি কেবল সাধারণ পূর্ণসংখ্যারই নয়, কাস্টম অনুলিপি নির্মাণকারী সহ আরও জটিল শ্রেণির উদাহরণ etc.

// A sample test class, with custom copy semantics.
class X
{
public:
    X() 
        : m_data(0) 
    {}

    X(int data)
        : m_data(data)
    {}

    ~X() 
    {}

    X(const X& other) 
        : m_data(other.m_data)
    { cout << "X copy ctor.\n"; }

    X& operator=(const X& other)
    {
        m_data = other.m_data;       
        cout << "X copy assign.\n";
        return *this;
    }

    int Get() const
    {
        return m_data;
    }

private:
    int m_data;
};

ostream& operator<<(ostream& os, const X& x)
{
    os << x.Get();
    return os;
}

যদি আমরা for (auto x : v) {...}এই নতুন শ্রেণীর সাথে উপরের সিনট্যাক্সটি ব্যবহার করি :

vector<X> v = {1, 3, 5, 7, 9};

cout << "\nElements:\n";
for (auto x : v)
{
    cout << x << ' ';
}

আউটপুট যেমন কিছু হয়:

[... copy constructor calls for vector<X> initialization ...]

Elements:
X copy ctor.
1 X copy ctor.
3 X copy ctor.
5 X copy ctor.
7 X copy ctor.
9

এটি আউটপুট থেকে পড়তে পারা যায়, লুপ পুনরাবৃত্তির জন্য পরিসর-ভিত্তিক অনুলিপি কন্সট্রাক্টর কল করা হয়।
এটি হ'ল কারণ আমরা ধারক থেকে উপাদানগুলি মান দ্বারা ক্যাপচার করছি theauto x অংশে for (auto x : v)) ।

এটি অকার্যকর কোড, উদাহরণস্বরূপ, যদি এই উপাদানগুলির উদাহরণ std::stringথাকে তবে মেমরি পরিচালকের কাছে ব্যয়বহুল ট্রিপ সহ হিপ মেমরির বরাদ্দ করা যেতে পারে This পালন একটি কন্টেইনারে উপাদান।

সুতরাং, একটি আরও ভাল বাক্য গঠন উপলব্ধ: রেফারেন্স দ্বারাconst ক্যাপচার , অর্থাত const auto&:

vector<X> v = {1, 3, 5, 7, 9};

cout << "\nElements:\n";
for (const auto& x : v)
{ 
    cout << x << ' ';
}

এখন আউটপুট হল:

 [... copy constructor calls for vector<X> initialization ...]

Elements:
1 3 5 7 9

কোনও উত্সাহী (এবং সম্ভাব্য ব্যয়বহুল) কপি নির্মাণকারী কল ছাড়াই।

সুতরাং যখন দেখে একটি ধারক (অর্থাত, কেবলমাত্র পাঠ্য অ্যাক্সেস জন্য) উপাদান, নিম্নলিখিত সিনট্যাক্স সহজ জন্য ভালো সস্তা টু কপি ধরনের, মত int, doubleইত্যাদি .:

for (auto elem : container) 

অন্যথায়, অনর্থক (এবং সম্ভাব্য ব্যয়বহুল) অনুলিপি নির্মাণকারী কল এড়াতে সাধারণ ক্ষেত্রেconst রেফারেন্স দ্বারা ক্যাপচার করা ভাল :

for (const auto& elem : container) 

পাত্রে উপাদানগুলি সংশোধন করা হচ্ছে

আমরা যদি পরিসীমা ভিত্তিক ব্যবহার করে একটি ধারকগুলিতে উপাদানগুলি সংশোধন করতে চাই forতবে উপরের for (auto elem : container)এবং for (const auto& elem : container)বাক্য গঠনগুলি ভুল।

প্রকৃতপক্ষে, পূর্ববর্তী ক্ষেত্রে, মূল উপাদানটির elemএকটি অনুলিপি সংরক্ষণ করে, সুতরাং এটি করা পরিবর্তনগুলি কেবল হারিয়ে যায় এবং ধারকটিতে অবিচ্ছিন্নভাবে সংরক্ষণ করা হয় না, যেমন:

vector<int> v = {1, 3, 5, 7, 9};
for (auto x : v)  // <-- capture by value (copy)
    x *= 10;      // <-- a local temporary copy ("x") is modified,
                  //     *not* the original vector element.

for (auto x : v)
    cout << x << ' ';

আউটপুট কেবল প্রাথমিক ক্রম:

1 3 5 7 9

পরিবর্তে, for (const auto& x : v)কেবল ব্যবহারের একটি প্রচেষ্টা সংকলন করতে ব্যর্থ।

g ++ একটি ত্রুটি বার্তাকে এরকম কিছু দেয়:

TestRangeFor.cpp:138:11: error: assignment of read-only reference 'x'
          x *= 10;
            ^

এই ক্ষেত্রে সঠিক পদ্ধতির constউল্লেখ ছাড়াই ক্যাপচার করা হয় :

vector<int> v = {1, 3, 5, 7, 9};
for (auto& x : v)
    x *= 10;

for (auto x : v)
    cout << x << ' ';

আউটপুটটি (আশানুরূপ হিসাবে):

10 30 50 70 90

এই for (auto& elem : container)বাক্য গঠন আরও জটিল ধরণের জন্য যেমন কাজ করে vector<string>:

vector<string> v = {"Bob", "Jeff", "Connie"};

// Modify elements in place: use "auto &"
for (auto& x : v)
    x = "Hi " + x + "!";

// Output elements (*observing* --> use "const auto&")
for (const auto& x : v)
    cout << x << ' ';

আউটপুটটি হ'ল:

Hi Bob! Hi Jeff! Hi Connie!

প্রক্সি পুনরাবৃত্তিকারীদের বিশেষ কেস

ধরা যাক আমাদের একটি আছে vector<bool>এবং আমরা উপরের সিনট্যাক্সটি ব্যবহার করে এর উপাদানগুলির লজিক্যাল বুলিয়ান রাষ্ট্রটিকে উল্টাতে চাই:

vector<bool> v = {true, false, false, true};
for (auto& x : v)
    x = !x;

উপরের কোডটি সংকলন করতে ব্যর্থ।

g ++ এর অনুরূপ একটি ত্রুটি বার্তা আউটপুট করে:

TestRangeFor.cpp:168:20: error: invalid initialization of non-const reference of
 type 'std::_Bit_reference&' from an rvalue of type 'std::_Bit_iterator::referen
ce {aka std::_Bit_reference}'
     for (auto& x : v)
                    ^

সমস্যা হল std::vectorটেমপ্লেট বিশেষ জন্য boolএকটি বাস্তবায়ন যে সঙ্গে, প্যাকগুলিbool অপ্টিমাইজ স্থান থেকে গুলি (প্রতিটি বুলিয়ান মান এক বিট, একটি বাইট আট "বুলিয়ান" বিট মধ্যে সংরক্ষিত হয়)।

এর কারণে (যেহেতু এটি কোনও একক রেফারেন্স ফেরানো সম্ভব নয়), vector<bool>তথাকথিত "প্রক্সি পুনরুত্থক" প্যাটার্ন ব্যবহার করে। একটি "প্রক্সি পুনরুত্থক" এমন একটি পুনরাবৃত্তি যা পুনঃনির্ধারণ করা হলে একটি সাধারণ ফল দেয় নাbool & , পরিবর্তে (মান অনুসারে) একটি অস্থায়ী বস্তু দেয় , যা প্রক্সি শ্রেণিতে রূপান্তরযোগ্যbool । ( এই প্রশ্ন এবং সম্পর্কিত উত্তরগুলিও দেখুন স্ট্যাকওভারফ্লোতেও দেখুন))

এর উপাদানগুলিকে পরিবর্তিত করতে vector<bool>, নতুন ধরণের সিনট্যাক্স (ব্যবহার auto&&) অবশ্যই ব্যবহার করা উচিত:

for (auto&& x : v)
    x = !x;

নিম্নলিখিত কোডটি সূক্ষ্মভাবে কাজ করে:

vector<bool> v = {true, false, false, true};

// Invert boolean status
for (auto&& x : v)  // <-- note use of "auto&&" for proxy iterators
    x = !x;

// Print new element values
cout << boolalpha;        
for (const auto& x : v)
    cout << x << ' ';

এবং ফলাফল:

false true true false

নোট করুন যে for (auto&& elem : container)বাক্য গঠনটি সাধারণ (নন-প্রক্সি) পুনরাবৃত্তির অন্যান্য ক্ষেত্রেও কাজ করে (উদাহরণস্বরূপ এ vector<int>বা এ এর জন্য)vector<string> )।

(পার্শ্ব নোট হিসাবে, for (const auto& elem : container)প্রক্সি পুনরাবৃত্তকারী ক্ষেত্রেও পূর্বোক্ত "পর্যবেক্ষণ" সিনট্যাক্সগুলি সূক্ষ্মভাবে কাজ করে))

সারসংক্ষেপ

উপরোক্ত আলোচনাটি নিম্নলিখিত নির্দেশিকাগুলিতে সংক্ষিপ্ত করা যেতে পারে:

  1. জন্য নিরীক্ষক উপাদান, নিম্নলিখিত সিনট্যাক্স ব্যবহার করুন:

    for (const auto& elem : container)    // capture by const reference
    • যদি বিষয়গুলি অনুলিপি করার জন্য সস্তা হয় (যেমন intগুলি, doubleগুলি ইত্যাদি) তবে কিছুটা সরলীকৃত ফর্ম ব্যবহার করা সম্ভব:

      for (auto elem : container)    // capture by value
  2. স্থানে থাকা উপাদানগুলিকে সংশোধন করার জন্য , ব্যবহার করুন:

    for (auto& elem : container)    // capture by (non-const) reference
    • যদি ধারকটি "প্রক্সি পুনরুক্তি"std::vector<bool> ব্যবহার করে (যেমন ), তবে ব্যবহার করুন:

      for (auto&& elem : container)    // capture by &&

অবশ্যই, যদি একটি করতে একটি প্রয়োজন নেই স্থানীয় অনুলিপি ক্যাপচার লুপ শরীর ভিতরে উপাদান, মান ( for (auto elem : container)) একটি ভাল পছন্দ।


জেনেরিক কোডে অতিরিক্ত নোট

ইন জেনেরিক কোড , যেহেতু আমরা জেনেরিক টাইপ সম্পর্কে অনুমানের করতে পারবেন না Tকপি সস্তা হচ্ছে, এ দেখে মোড এটা সবসময় ব্যবহার করা নিরাপদ for (const auto& elem : container)
(এটি সম্ভাব্য ব্যয়বহুল অনর্থক অনুলিপিগুলি ট্রিগার করবে না, সস্তা-টু-কপি প্রকারের intজন্য এবং প্রক্সি-পুনরায় ব্যবহারকারী পাত্রে যেমন , ঠিক তেমন কাজ করবেstd::vector<bool> ))

তদুপরি, সংশোধন মোডে, আমরা যদি প্রক্সি-পুনরুক্তির ক্ষেত্রে জেনেরিক কোডটি কাজ করতে চাই, তবে সর্বোত্তম বিকল্পটি for (auto&& elem : container)
(এই সাধারণ অ প্রক্সি-iterators ব্যবহার করে, মত পাত্রে জন্য শুধু জরিমানা এছাড়াও কাজ করবে std::vector<int>বা std::vector<string>।)

সুতরাং, জেনেরিক কোডে , নিম্নলিখিত নির্দেশিকাগুলি সরবরাহ করা যেতে পারে:

  1. জন্য নিরীক্ষক উপাদান, ব্যবহার করুন:

    for (const auto& elem : container)
  2. স্থানে থাকা উপাদানগুলিকে সংশোধন করার জন্য , ব্যবহার করুন:

    for (auto&& elem : container)

7
জেনেরিক প্রসঙ্গে কোন পরামর্শ নেই? :(
আর মার্টিনহো ফার্নান্দেস

11
সবসময় ব্যবহার auto&&করবেন না কেন ? একটি আছে const auto&&?
মার্টিন বা

1
আমার ধারণা আপনি লুপটির ভিতরে একটি অনুলিপিটির প্রয়োজন নেই এমন ক্ষেত্রে আপনি কী মিস করছেন?
জুয়ানচোপাঞ্জা

6
"যদি ধারকটি" প্রক্সি পুনরাবৃত্তিকারী "" ব্যবহার করে - এবং আপনি জানেন যে এটি "প্রক্সি পুনরাবৃত্তিকারী" ব্যবহার করেন (যা জেনেরিক কোডের ক্ষেত্রে নাও হতে পারে)। সুতরাং আমি মনে করি সেরাটি প্রকৃত পক্ষে auto&&, যেহেতু এটি auto&সমানভাবে ভালভাবে কভার করে।
খ্রিস্টান রাউ

5
আপনাকে ধন্যবাদ, এটি একটি সি # প্রোগ্রামারের জন্য সিনট্যাক্সের সিনট্যাক্সের জন্য সত্যই দুর্দান্ত "ক্র্যাশ কোর্স পরিচিতি" এবং রেঞ্জের জন্য কিছু টিপস ছিল। +1 টি।
অ্যান্ড্রুজ্যাকসনজেড এজে

17

ব্যবহারের সঠিক কোনও উপায় নেই for (auto elem : container), for (auto& elem : container)বা for (const auto& elem : container)। আপনি যা চান তা প্রকাশ করুন।

আমাকে এ সম্পর্কে বিস্তারিত জানাতে দিন। আসুন একটি ঘুরতে।

for (auto elem : container) ...

এটি একটির জন্য সিনট্যাকটিক চিনি:

for(auto it = container.begin(); it != container.end(); ++it) {

    // Observe that this is a copy by value.
    auto elem = *it;

}

আপনার ধারকটিতে এমন উপাদান রয়েছে যা অনুলিপি করা সস্তা You

for (auto& elem : container) ...

এটি একটির জন্য সিনট্যাকটিক চিনি:

for(auto it = container.begin(); it != container.end(); ++it) {

    // Now you're directly modifying the elements
    // because elem is an lvalue reference
    auto& elem = *it;

}

উদাহরণস্বরূপ আপনি যখন ধারকটিতে থাকা উপাদানগুলিতে সরাসরি লিখতে চান তখন এটি ব্যবহার করুন।

for (const auto& elem : container) ...

এটি একটির জন্য সিনট্যাকটিক চিনি:

for(auto it = container.begin(); it != container.end(); ++it) {

    // You just want to read stuff, no modification
    const auto& elem = *it;

}

মন্তব্য যেমনটি বলেছে, কেবল পড়ার জন্য। এবং এটি সম্পর্কে, সবকিছু সঠিকভাবে ব্যবহার করার সময় "সঠিক"।


2
আমি নমুনা কোডগুলি সংকলন (তবে অদক্ষ হওয়া), বা সংকলন করতে ব্যর্থ, এবং কেন ব্যাখ্যা করছি এবং কিছু সমাধানের প্রস্তাব দেওয়ার চেষ্টা করে কিছু গাইডেন্স দেওয়ার চেষ্টা করেছি।
মিঃসি 64

2
@ মিঃ সি 6464 ওহ, আমি দুঃখিত - আমি সবেমাত্র লক্ষ্য করেছি যে এগুলি এফএকিউ-টাইপ প্রশ্নগুলির মধ্যে একটি। আমি এই সাইটে নতুন। দুঃক্ষিত! আপনার উত্তরটি দুর্দান্ত, আমি এটিকে উজ্জ্বল করেছিলাম - তবে যারা এটির সূচনা চান তাদের জন্য আরও সংক্ষিপ্ত সংস্করণ সরবরাহ করতে চেয়েছিলেন । আশা করি, আমি অনুপ্রবেশ করছি না।

1
@ মিঃ সি 6464, ওপিতে প্রশ্নের উত্তর দেওয়ার ক্ষেত্রে কী সমস্যা? এটি ঠিক অন্যটি, বৈধ, উত্তর।
mfontanini

1
@ এমফোঁটানিনি: আমার উত্তর থেকে কেউ আরও কিছু উত্তর পোস্ট করলে একেবারেই সমস্যা নেই। চূড়ান্ত উদ্দেশ্যটি হ'ল সম্প্রদায়কে একটি গুণমানের অবদান দেওয়া (বিশেষত শুরুর জন্য যারা সি ++ অফার করে বিভিন্ন সিনট্যাক্স এবং বিভিন্ন বিকল্পের সামনে একরকম হারিয়ে যেতে পারেন)।
মিঃসি 64

4

সঠিক উপায় সর্বদা

for(auto&& elem : container)

এটি সমস্ত শব্দার্থ সংরক্ষণের গ্যারান্টি দেবে।


6
তবে কি তবে যদি ধারক কেবল পরিবর্তিতযোগ্য রেফারেন্সগুলি ফেরত দেয় এবং আমি পরিষ্কার করতে চাই যে আমি লুপে সেগুলি সংশোধন করতে চাই না? আমি কি তখন auto const &আমার উদ্দেশ্য পরিষ্কার করতে ব্যবহার করব না?
রেডএক্স

@ রেডএক্স: একটি "সংশোধনযোগ্য রেফারেন্স" কী?
অরবিট

2
@ রেডএক্স: তথ্যসূত্রগুলি কখনই হয় না constএবং সেগুলি কখনও পরিবর্তনযোগ্য হয় না। যাইহোক, আপনার আমার উত্তর হ্যাঁ, আমি চাই
অরবিট

4
যদিও এটি কার্যকর হতে পারে, আমি মনে করি যে মিঃ সি 6464 এর সর্বোত্তম এবং বিস্তৃত উত্তরের উপরোক্ত প্রদত্ত আরও সংখ্যক বিবেচিত এবং বিবেচিত পদ্ধতির তুলনায় এটি দুর্বল পরামর্শ। সর্বনিম্ন সাধারণ ডিনোমিনেটরকে হ্রাস করা সি ++ এর জন্য নয়।
জ্যাক এইডলি

6
এই ভাষা বিবর্তনের প্রস্তাব এই "দুর্বল" উত্তরের সাথে একমত: open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3853.htm
লুস হার্মিটে

1

যদিও পরিসরের জন্য লুপটির প্রাথমিক প্রেরণাটি কোনও ধারকগুলির উপাদানগুলির উপর পুনরাবৃত্তি করা সহজ হতে পারে তবে সিনট্যাক্সটি সাধারণভাবে পাত্রে নয় এমন বস্তুর জন্যও যথেষ্ট কার্যকর।

লুপটির জন্য সিনট্যাকটিক প্রয়োজনীয়তা হ'ল range_expressionসমর্থন begin()এবং end()উভয় ফাংশন - এটি যে ধরণের সদস্য ফাংশন হিসাবে মূল্যায়ন করে তা বা অ-সদস্য ক্রিয়াকলাপ হিসাবে যা টাইপের উদাহরণ দেয়।

স্বীকৃত উদাহরণ হিসাবে, কেউ নিম্নলিখিত শ্রেণিটি ব্যবহার করে বিস্তৃত সংখ্যা তৈরি করতে পারে এবং পরিসীমাটির উপরে পুনরাবৃত্তি করতে পারে।

struct Range
{
   struct Iterator
   {
      Iterator(int v, int s) : val(v), step(s) {}

      int operator*() const
      {
         return val;
      }

      Iterator& operator++()
      {
         val += step;
         return *this;
      }

      bool operator!=(Iterator const& rhs) const
      {
         return (this->val < rhs.val);
      }

      int val;
      int step;
   };

   Range(int l, int h, int s=1) : low(l), high(h), step(s) {}

   Iterator begin() const
   {
      return Iterator(low, step);
   }

   Iterator end() const
   {
      return Iterator(high, 1);
   }

   int low, high, step;
}; 

নিম্নলিখিত mainফাংশন সহ,

#include <iostream>

int main()
{
   Range r1(1, 10);
   for ( auto item : r1 )
   {
      std::cout << item << " ";
   }
   std::cout << std::endl;

   Range r2(1, 20, 2);
   for ( auto item : r2 )
   {
      std::cout << item << " ";
   }
   std::cout << std::endl;

   Range r3(1, 20, 3);
   for ( auto item : r3 )
   {
      std::cout << item << " ";
   }
   std::cout << std::endl;
}

এক নিম্নলিখিত আউটপুট পাবেন।

1 2 3 4 5 6 7 8 9 
1 3 5 7 9 11 13 15 17 19 
1 4 7 10 13 16 19 
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.