পাস-বাই-ভ্যালু এবং স্টাড :: এর সুবিধাগুলি পাস-বাই-রেফারেন্সের উপরে চলে যায়


105

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

আমি এই ক্লাসটি টিউটোরিয়াল থেকে ব্যবহার করেছি:

class Creature
{
private:
    std::string m_name;

public:
    Creature(const std::string &name)
            :  m_name{name}
    {
    }
};

এটি ঝনঝন-পরিচ্ছন্নতার একটি পরামর্শের দিকে পরিচালিত করে যে রেফারেন্স এবং ব্যবহারের পরিবর্তে মান দিয়ে আমার পাস করা উচিত std::move। যদি আমি এটি করি, আমি nameএকটি রেফারেন্স তৈরি করার পরামর্শটি পেয়েছি (এটি প্রতিবারের মতো অনুলিপি করা হয় না তা নিশ্চিত করার জন্য) এবং সতর্কবার্তাটির std::moveকোনও প্রভাব পড়বে না কারণ nameএটি constআমার অপসারণ করা উচিত।

আমি সতর্কতা না পাওয়ার একমাত্র উপায় হ'ল constসম্পূর্ণ অপসারণ :

Creature(std::string name)
        :  m_name{std::move(name)}
{
}

যৌক্তিক বলে মনে হয়, কারণ এর একমাত্র সুবিধাটি constছিল মূল স্ট্রিংয়ের সাথে জগাখিচু করা রোধ করা (যা ঘটেছিল না কারণ আমি মূল্য দিয়ে পাশ করেছি)। তবে আমি সিপ্লাসপ্লাস.কমপড়েছি :

যদিও নোট করুন যে-প্রমিত লাইব্রেরিতে- চলন্ত বোঝায় যে স্থানান্তরিত বস্তুটি একটি বৈধ তবে অনির্ধারিত অবস্থায় রেখে গেছে। যার অর্থ হ'ল, এই জাতীয় অপারেশনের পরে, স্থানান্তরিত বস্তুর মানটি কেবল ধ্বংস করা বা একটি নতুন মান নির্ধারণ করা উচিত; এটি অ্যাক্সেস অন্যথায় একটি অনির্ধারিত মান দেয়।

এখন এই কোডটি কল্পনা করুন:

std::string nameString("Alex");
Creature c(nameString);

যেহেতু nameStringমান দ্বারা পাস হয়ে যায়, std::moveকেবল nameকনস্ট্রাক্টরের অভ্যন্তরে অকার্যকর হবে এবং মূল স্ট্রিংটিকে স্পর্শ করবে না। তবে এর সুবিধা কী? দেখে মনে হচ্ছে বিষয়গুলি কেবল একবারই অনুলিপি করা হয়েছে - যদি আমি কল করার সময় m_name{name}যদি রেফারেন্স দিয়ে যাই , আমি পাস করার সময় যদি আমি মূল্য দিয়ে পাস করি (এবং তারপরে এটি সরানো হয়ে যায়)। আমি বুঝতে পারি যে এটি মান দ্বারা পাস করা এবং ব্যবহার না করার চেয়ে ভাল std::move(কারণ এটি দুইবার অনুলিপি হয়ে যায়)।

সুতরাং দুটি প্রশ্ন:

  1. আমি কি এখানে সঠিকভাবে বুঝতে পারি?
  2. std::moveরেফারেন্স এবং কেবল কল দিয়ে ওভার ব্যবহার করার কোনও উল্টোপাল্টা আছে m_name{name}?

3
রেফারেন্স দ্বারা পাস করার সাথে Creature c("John");একটি অতিরিক্ত অনুলিপি তৈরি করে
ব্যবহারকারীর 253751

1
এই লিঙ্কটি একটি মূল্যবান পঠন হতে পারে, এটি পাসিং std::string_viewএবং এসএসওকেও কভার করে।
lubgr

আমি clang-tidyপঠনযোগ্যতা ব্যয় করে অপ্রয়োজনীয় মাইক্রোপটিমাইজেশনগুলি সম্পর্কে নিজেকে আচ্ছন্ন করার একটি দুর্দান্ত উপায় খুঁজে পেয়েছি । এখানে অন্য যে কোনও কিছুর আগে জিজ্ঞাসা করার প্রশ্নটি হ'ল আমরা আসলে কতবার Creatureকনস্ট্রাক্টরকে কল করি ।
সিজেড

উত্তর:


36
  1. আমি কি এখানে সঠিকভাবে বুঝতে পারি?

হ্যাঁ.

  1. std::moveরেফারেন্স এবং কেবল কল দিয়ে ওভার ব্যবহার করার কোনও উল্টোপাল্টা আছে m_name{name}?

কোনও অতিরিক্ত ওভারলোড ছাড়াই ফাংশনের স্বাক্ষর উপলব্ধি করা সহজ। স্বাক্ষরটি তত্ক্ষণাত প্রকাশ করে যে যুক্তিটি অনুলিপি করা হবে - এটি কলকারীদের এই ভাবনা থেকে বাঁচায় যে কোনও const std::string&রেফারেন্স ডেটা সদস্য হিসাবে সংরক্ষণ করা যেতে পারে, সম্ভবত পরে ঝুঁকির রেফারেন্স হয়ে উঠবে। ফাংশনে যখন মূল্যগুলি পাস করা হয় তখন অপ্রয়োজনীয় অনুলিপিগুলি এড়াতে ওভারলোড std::string&& nameএবং const std::string&তর্ক করার দরকার নেই । একটি মূল্য মূল্য পাস

std::string nameString("Alex");
Creature c(nameString);

মান অনুসারে তার যুক্তিটি গ্রহণ করে এমন ক্রিয়াকলাপে একটি অনুলিপি এবং একটি সরানো নির্মাণের কারণ হয়। একই কার্যক্রমে একটি মূল্য পাস হচ্ছে

std::string nameString("Alex");
Creature c(std::move(nameString));

দুটি সরানো নির্মাণ কারণ। বিপরীতে, যখন ফাংশন প্যারামিটার হয় const std::string&, সর্বদা একটি অনুলিপি থাকবে, এমনকি কোনও মূল্যের যুক্তিটি পাস করার পরেও। যতক্ষণ না আর্গুমেন্ট টাইপটি মুভ-কনস্ট্রাক্টের জন্য সস্তা (এটি এটি ক্ষেত্রে std::string) ততক্ষণ স্পষ্টভাবে এটি একটি সুবিধা ।

তবে বিবেচনা করার মতো একটি পার্থক্য রয়েছে: যুক্তিটি ফাংশনগুলির পক্ষে কাজ করে না যা ফাংশন আর্গুমেন্টকে অন্য ভেরিয়েবলের জন্য বরাদ্দ করে (এটি শুরু করার পরিবর্তে):

void setName(std::string name)
{
    m_name = std::move(name);
}

সংস্থানটি m_nameপুনর্নির্ধারণের আগে উল্লেখ করা সংস্থার হ্রাস ঘটায় । আমি কার্যকর আধুনিক সি ++ এ আইটেম 41 পড়ার পরামর্শ দিচ্ছি এবং এই প্রশ্নটিও


এটি অর্থবোধ করে, বিশেষত যে এটি ঘোষণাকে আরও স্বজ্ঞাত করে তোলে। আমি নিশ্চিত নই যে আমি আপনার উত্তরের ডিওলোকেশন অংশটি পুরোপুরি উপলব্ধি করেছি (এবং সংযুক্ত থ্রেডটি বুঝেছি), তাই আমি যদি ব্যবহার করি moveতবে তা স্থানটি হ্রাস পেয়েছে তা পরীক্ষা করে দেখুন । যদি আমি moveএটি ব্যবহার না করি তবে কেবলমাত্র নতুন স্ট্রিং ধরে রাখার জন্য বরাদ্দ স্থানটি খুব কম হলে এটি ডিলোক্যাট হয়ে যায়, উন্নত কর্মক্ষমতা বাড়ে। এটা কি ঠিক?
ব্ল্যাকবট

1
হ্যাঁ, এটা ঠিক। m_nameএকটি const std::string&প্যারামিটার থেকে বরাদ্দ করার সময় , অভ্যন্তরীণ মেমরিটি যতক্ষণ m_nameনা ফিট হয় ততক্ষণ পুনরায় ব্যবহার করা হয় When যখন সরানো- m_nameবরাদ্দকরণ করার সময়, মেমরিটি আগেই ডিলোক্যাট করা উচিত। অন্যথায়, অ্যাসাইনমেন্টের ডান হাত থেকে সংস্থানগুলি "চুরি" করা অসম্ভব ছিল।
lubgr

এটি কখন ঝোঁকানো রেফারেন্সে পরিণত হয়? আমি মনে করি ইনিশিয়েশন তালিকাটি গভীর অনুলিপি ব্যবহার করে।
লি তাইজি

100
/* (0) */ 
Creature(const std::string &name) : m_name{name} { }
  • একটি পাস করা ল্যাভেলু বেঁধে দেয় name, তারপরে অনুলিপি করা হয় m_name

  • একটি উত্তীর্ণের অনুচ্ছেদে আবদ্ধ হয় name, তারপরে অনুলিপি করা হয় m_name


/* (1) */ 
Creature(std::string name) : m_name{std::move(name)} { }
  • একজন পাস lvalue হয় কপি মধ্যে name, তারপর সরানো মধ্যে m_name

  • একজন পাস rvalue হয় সরানো মধ্যে name, তারপর সরানো মধ্যে m_name


/* (2) */ 
Creature(const std::string &name) : m_name{name} { }
Creature(std::string &&rname) : m_name{std::move(rname)} { }
  • একটি পাস করা ল্যাভেলু বেঁধে দেয় name, তারপরে অনুলিপি করা হয় m_name

  • একটি উত্তীর্ণের অনুচ্ছেদে বাঁধা rname, তারপরে সরানো হয় m_name


যেহেতু মুভি অপারেশনগুলি সাধারণত অনুলিপিগুলির চেয়ে দ্রুত হয়, (1) এর চেয়ে ভাল (0) যদি আপনি প্রচুর অস্থায়ী পাস করেন। (২) অনুলিপি / চলনের ক্ষেত্রে অনুকূল, তবে কোড পুনরাবৃত্তি প্রয়োজন।

নিখুঁত ফরোয়ার্ডিং সহ কোড পুনরাবৃত্তি এড়ানো যায় :

/* (3) */
template <typename T,
          std::enable_if_t<
              std::is_convertible_v<std::remove_cvref_t<T>, std::string>, 
          int> = 0
         >
Creature(T&& name) : m_name{std::forward<T>(name)} { }

Tএই কন্সট্রাক্টর দ্বারা (উপরে দেখানো হিসাবে) ইনস্ট্যান্ট করা যেতে পারে এমন প্রকারের ডোমেনটিকে সীমাবদ্ধ করতে আপনি optionচ্ছিকভাবে সীমাবদ্ধ রাখতে চাইতে পারেন । সি ++ ২০ এর লক্ষ্যটি ধারণাগুলি দিয়ে এটি সরল করা ।


সি ++ ১ In-এ , গ্যারান্টিযুক্ত অনুলিপি দ্বারা মূল্যগুলি প্রভাবিত হয় - যা প্রযোজ্য হলে - ফাংশনে আর্গুমেন্টগুলি পাস করার সময় অনুলিপি / চালনার সংখ্যা হ্রাস করবে।


(1) এর জন্য PR + মান এবং xvalue কেস সি ++ 17 নম্বর থেকে অভিন্ন নয়?
অলিভ

1
মনে রাখবেন যে এক্ষেত্রে আপনার এগিয়ে যাওয়ার জন্য SFINAE দরকার নেই । এটি কেবল নিরস্ত করা প্রয়োজন। এটা -এর সম্ভাব্য সম্ভাব্য ত্রুটি বার্তা যখন খারাপ আর্গুমেন্ট ক্ষণস্থায়ী জন্য সহায়ক
Caleth

@ অলিভ হ্যাঁ এক্সভ্যালুগুলি স্থানান্তরিত করা দরকার, যখন মূল্যবৃত্তগুলি ইলিডেড করা যেতে পারে :)
রেকেট 1111

1
আমরা লিখতে পারে না: Creature(const std::string &name) : m_name{std::move(name)} { }মধ্যে (2) ?
স্কাইট্রি

4
@ স্কাইটি্রি: আপনি কোনও কনস্টেন্ট অবজেক্ট থেকে সরাতে পারবেন না, কারণ চলন্ত উত্সকে পরিবর্তিত করে। এটি সংকলন করবে, তবে এটি একটি অনুলিপি তৈরি করবে।
ভিটোরিও রোমিও

1

আপনি কীভাবে পাস করবেন তা এখানে কেবল পরিবর্তনশীল নয়, আপনি যা পাস করেন তা উভয়ের মধ্যে বড় পার্থক্য তৈরি করে।

C ++, আমরা মান আরও সব ধরণের এবং এই "বাগ্ধারা" ক্ষেত্রে যেখানে আপনি একটি মধ্যে পাস জন্য বিদ্যমান rvalue (যেমন "Alex-string-literal-that-constructs-temporary-std::string"অথবা std::move(nameString)), যা ফলাফল 0 কপি এর std::stringতৈরি করা হচ্ছে (টাইপ এমনকি নেই কপি-অঙ্কনযোগ্য হতে মূল্যবান আর্গুমেন্টের জন্য), এবং কেবলমাত্র std::stringসরানো কনস্ট্রাক্টর ব্যবহার করে।

কিছুটা সম্পর্কিত প্রশ্নোত্তর


1

পাস-বাই- (আরভি) রেফারেন্সের তুলনায় পাস-বাই-মান-এবং-মুভ পদ্ধতির বিভিন্ন অসুবিধা রয়েছে:

  • এটি 2 এর পরিবর্তে 3 অবজেক্ট তৈরি করে;
  • মান দ্বারা কোনও বস্তু পাস করা অতিরিক্ত স্ট্যাক ওভারহেডের দিকে নিয়ে যেতে পারে, কারণ এমনকি নিয়মিত স্ট্রিং ক্লাসটি সাধারণত পয়েন্টারের চেয়ে কমপক্ষে 3 বা 4 গুণ বড় হয়;
  • আর্গুমেন্ট অবজেক্টস নির্মাণকারীর পক্ষ থেকে কলার দিকে কাজ চলছে, যার ফলে কোড ব্লাট;

আপনি কেন স্পষ্ট করে বলতে পারেন যে এটির কারণে 3 টি বস্তুর স্পোন হবে? আমি যা বুঝি সেখান থেকে আমি কেবল "পিটার" কে স্ট্রিং হিসাবে পাস করতে পারি। এটি তৈরি হবে, অনুলিপি হবে এবং তারপরে সরানো হবে, তাই না? এবং স্ট্যাকটি কোনও পর্যায়ে নির্বিশেষে ব্যবহৃত হবে না? কনস্ট্রাক্টর কলের বিন্দুতে নয়, তবে যে m_name{name}অংশে এটি অনুলিপি করা হয়েছে?
ব্ল্যাকবট

@ ব্ল্যাকবট আমি আপনার উদাহরণের সাথে উল্লেখ করছিলাম std::string nameString("Alex"); Creature c(nameString);একটি বস্তু হ'ল nameString, অন্যটি হ'ল ফাংশন আর্গুমেন্ট এবং তৃতীয়টি একটি শ্রেণির ক্ষেত্র।
ব্যবহারকারীর 7860670

0

আমার ক্ষেত্রে, মান অনুসারে স্যুইচ করা এবং তারপরে একটি স্টাডি: মুভের ফলে ঠিকানা স্যানিটাইজারে একটি হিপ-ব্যবহার-পরে-ত্রুটি হয়েছিল।

https://travis-ci.org/github/acgetchell/CDT-plusplus/jobs/679520360#L3165

সুতরাং, আমি এটিকে বন্ধ করে দিয়েছি, পাশাপাশি ঝনঝন-পরিচ্ছন্নতার পরামর্শও।

https://github.com/acgetchell/CDT-plusplus/compare/80c96789f0a2...0d78fd63b332

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.