পাস-বাই-ভ্যালু কি সি ++ 11 এ যুক্তিসঙ্গত ডিফল্ট?


142

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

এখন, সি ++ ১১-এ, আমরা মূল্যবোধের রেফারেন্স এবং মুভ কনস্ট্রাক্টর পেয়েছি যার অর্থ std::vectorএকটি ফাংশনের মধ্যে এবং মূল্য থেকে বেরিয়ে আসা সস্তা যে কোনও বৃহত অবজেক্ট (এর মতো ) কার্যকর করা সম্ভব ।

সুতরাং, এর অর্থ কি ডিফল্টরূপে যেমন std::vectorএবং এর উদাহরণগুলির জন্য মান দ্বারা পাস করা উচিত std::string? কাস্টম অবজেক্টের জন্য কী? নতুন সেরা অনুশীলন কি?


22
pass by reference ... which introduces all sorts of complicated questions around ownership and especially around memory management (in the event that the object is heap-allocated)। আমি বুঝতে পারি না মালিকানার পক্ষে এটি কীভাবে জটিল বা সমস্যা? আমি কি কিছু মিস করতে পারি?
iammilind

1
@ আইয়ামিলিন্ড: ব্যক্তিগত অভিজ্ঞতা থেকে একটি উদাহরণ। একটি থ্রেড একটি স্ট্রিং অবজেক্ট আছে। এটি এমন একটি ফাংশনে পাঠানো হয়েছে যা অন্য থ্রেড তৈরি করে, তবে কলারের কাছে অজানা ফাংশনটি স্ট্রিংটি নিয়েছিল const std::string&এবং অনুলিপি হিসাবে নয়। এর পরে প্রথম থ্রেডটি বেরিয়েছে ...
ঝ্যান লিংকস

12
@ জ্যানলিনেক্স: এটি এমন কোনও ফাংশনের মতো শোনাচ্ছে যা স্পষ্টভাবে কখনও থ্রেড ফাংশন হিসাবে ডাকা যায়নি।
নিকল বোলাস

5
আইমিলিন্ডের সাথে একমত হয়ে আমি কোনও সমস্যা দেখছি না। "বড়" অবজেক্টের জন্য কনস্টের রেফারেন্স দিয়ে পাস করা আপনার ডিফল্ট হওয়া উচিত এবং ছোট ছোট অবজেক্টের জন্য মান হিসাবে। আমি বড় এবং ছোটের মধ্যে সীমাটি প্রায় 16 বাইটে (বা 32 বিট সিস্টেমে 4 পয়েন্টার) রেখে দিতাম।
জেএন

3
বুনিয়াদীতে হারব সুটারের পিছনে! CppCon এ মডার্ন সি ++ উপস্থাপনার প্রয়োজনীয়তা এ সম্পর্কে বেশ কিছুটা বিশদে গিয়েছিল। ভিডিও এখানে
ক্রিস ড্রু

উত্তর:


138

আপনার যদি শরীরের অভ্যন্তরে কোনও অনুলিপি তৈরি করতে হয় তবে এটি যুক্তিযুক্ত ডিফল্ট । ডেভ আবরহামস এটিই সমর্থন করছেন :

গাইডলাইন: আপনার ফাংশন যুক্তি অনুলিপি করবেন না। পরিবর্তে, তাদের মান দ্বারা পাস এবং সংকলক অনুলিপি করতে দিন।

কোডে এর অর্থ এটি করবেন না:

void foo(T const& t)
{
    auto copy = t;
    // ...
}

তবে এটি করুন:

void foo(T t)
{
    // ...
}

যার সুবিধা রয়েছে যে কলার এর fooমতো ব্যবহার করতে পারে :

T lval;
foo(lval); // copy from lvalue
foo(T {}); // (potential) move from prvalue
foo(std::move(lval)); // (potential) move from xvalue

এবং শুধুমাত্র সর্বনিম্ন কাজ সম্পন্ন হয়। আপনি রেফারেন্স সঙ্গে একই করার দুটি overloads প্রয়োজন চাই, void foo(T const&);এবং void foo(T&&);

এই বিষয়টি মাথায় রেখে আমি এখন আমার মূল্যবান নির্মাতাদের যেমন লিখেছি:

class T {
    U u;
    V v;
public:
    T(U u, V v)
        : u(std::move(u))
        , v(std::move(v))
    {}
};

অন্যথায়, constএখনও রেফারেন্স দ্বারা পাস যুক্তিসঙ্গত।


29
+1, বিশেষত শেষ বিটের জন্য :) একটিকে ভুলে যাওয়া উচিত নয় যে মুভ কনস্ট্রাক্টরগুলি কেবল তখনই আহ্বান করা যেতে পারে যদি সেখান থেকে সরানো অবজেক্টটি পরে পরিবর্তিত হবে SomeProperty p; for (auto x: vec) { x.foo(p); }না : উদাহরণস্বরূপ, ফিট হয় না fit এছাড়াও, মুভ কনস্ট্রাক্টরগুলির মূল্যে const&নিখরচায় একটি ব্যয় হয় (বস্তুটি যত বড়, তত বেশি দাম) ।
ম্যাথিউ এম।

25
@MatthieuM। তবে এই পদক্ষেপের "বৃহত্তর বস্তু, উচ্চ ব্যয়" এর অর্থ কী তা জানা গুরুত্বপূর্ণ: "বৃহত্তর" আসলে "তার আরও বেশি সদস্যের ভেরিয়েবলগুলি" এর অর্থ। উদাহরণস্বরূপ, std::vectorএক মিলিয়ন উপাদানের সাথে একটি স্থানান্তরিত করতে পাঁচটি উপাদানের সাথে একটি সরানোর সমান ব্যয় হয় যেহেতু গাদাতে থাকা অ্যারেতে কেবল পয়েন্টারটি সরানো হয়, ভেক্টরের প্রতিটি বস্তু নয়। সুতরাং এটি আসলে কোনও সমস্যার বড় নয় big
লুকাস

+1 আমি সি ++ 11 ব্যবহার শুরু করার পরে পাস-বাই-মান-তারপর-স্থানান্তর কন্সট্রাক্টও ব্যবহার করার ঝোঁক। এটি আমার মনে কিছুটা অস্বস্তিকর যদিও, আমার কোড এখন যেহেতু তোলে std::moveসব জায়গায় বেশি ..
stijn

1
এর সাথে একটি ঝুঁকি রয়েছে const&, যা আমাকে কয়েকবার ছড়িয়ে দিয়েছে। void foo(const T&); int main() { S s; foo(s); }। প্রকারগুলি ভিন্ন হলেও এটি সংকলন করতে পারে, যদি কোনও টি নির্মাণকারী থাকে যা কোনও এসকে যুক্তি হিসাবে গ্রহণ করে। এটি ধীর হতে পারে, কারণ একটি বৃহত টি অবজেক্টটি নির্মাণ করা হতে পারে। আপনি অনুলিপি ছাড়াই আপনার রেফারেন্সটি পাস করার কথা ভাবতে পারেন তবে আপনি হতে পারেন। দেখুন একটি প্রশ্ন আমি জিজ্ঞেস করলাম এই উত্তর আরো অনেক কিছুর জন্য। মূলত, &সাধারণত শুধুমাত্র লভ্যগুলিকে আবদ্ধ করে, তবে এর ব্যতিক্রমও রয়েছে rvalue। বিকল্প আছে।
অ্যারোন ম্যাকডেইড

1
@ অ্যারোন এমসিএডইড এটি পুরানো সংবাদ, এই অর্থে এটি আপনাকে সি ++ 11 এর আগেও সর্বদা সচেতন হতে হয়েছিল। এবং যে সম্মানের সাথে তেমন কিছুই পরিবর্তন হয় নি।
লুক ড্যান্টন

71

প্রায় সব ক্ষেত্রেই, আপনার শব্দার্থবিজ্ঞানের একটি হওয়া উচিত:

bar(foo f); // want to obtain a copy of f
bar(const foo& f); // want to read f
bar(foo& f); // want to modify f

অন্যান্য সমস্ত স্বাক্ষর কেবল অল্প পরিমাণে এবং ভাল ন্যায়সঙ্গত ব্যবহার করা উচিত। সংকলকটি এখন বেশ কার্যকরভাবে সর্বদা কার্যকরভাবে কাজ করবে। আপনি কেবল আপনার কোড লেখার সাথে চালিয়ে যেতে পারেন!


2
যদিও আমি একটি যুক্তি সংশোধন করতে যাচ্ছি যদি আমি একটি পয়েন্টার পাস করা পছন্দ করি। আমি গুগল স্টাইল গাইডের সাথে একমত আছি যে এটি আরও স্পষ্ট করে তোলে যে ফাংশনের স্বাক্ষর ( google-styleguide.googlecode.com/svn/trunk/… ) ডাবল-চেক না করে যুক্তিটি সংশোধন করা হবে ।
ম্যাক্স লাইববার্ট

40
আমি পয়েন্টারগুলি পাস করা অপছন্দ করার কারণটি হ'ল এটি আমার ফাংশনগুলিতে একটি সম্ভাব্য ব্যর্থতার রাষ্ট্র যুক্ত করে। আমি আমার সব ফাংশন লিখতে, যাতে তারা provably সঠিক হয়, যেমন অতি গোপন করতে বাগ জন্য স্থান হ্রাস করার চেষ্টা করুন। foo(bar& x) { x.a = 3; }চেয়ে অধিক নির্ভরযোগ্য (এবং পাঠযোগ্য!) অনেক একটি নরক হয়foo(bar* x) {if (!x) throw std::invalid_argument("x"); x->a = 3;
Ayjay

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

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

1
@ অ্যারোনএমসিডেইড এই is shared_ptr intended to never be null? Much as (I think) unique_ptr is?দুটি অনুমানই ভুল। unique_ptrএবং shared_ptrনাল / nullptrমান ধরে রাখতে পারে । আপনি যদি নাল মান সম্পর্কে উদ্বিগ্ন না হতে চান তবে আপনার উল্লেখগুলি ব্যবহার করা উচিত, কারণ সেগুলি কখনই নਾਲ হতে পারে না। ->
জুলিয়ান

10

যদি ফাংশন বডিটির ভিতরে আপনার অবজেক্টের একটি অনুলিপি প্রয়োজন হয় বা কেবল অবজেক্টটি সরানো দরকার হয় তবে মান দ্বারা পরামিতিগুলি পাস করুন। const&আপনার যদি কেবলমাত্র অবজেক্টটিতে অ-পরিবর্তিত অ্যাক্সেসের প্রয়োজন হয় তবে পাশ করুন ।

বস্তুর অনুলিপি উদাহরণ:

void copy_antipattern(T const& t) { // (Don't do this.)
    auto copy = t;
    t.some_mutating_function();
}

void copy_pattern(T t) { // (Do this instead.)
    t.some_mutating_function();
}

বস্তুর সরানোর উদাহরণ:

std::vector<T> v; 

void move_antipattern(T const& t) {
    v.push_back(t); 
}

void move_pattern(T t) {
    v.push_back(std::move(t)); 
}

অ-পরিবর্তিত অ্যাক্সেসের উদাহরণ:

void read_pattern(T const& t) {
    t.some_const_function();
}

যৌক্তিকতার জন্য ডেভ আব্রাহামস এবং জিয়াং ফ্যানের এই ব্লগ পোস্টগুলি দেখুন ।


0

কোনও ফাংশনের স্বাক্ষরটি এর উদ্দেশ্যযুক্ত ব্যবহারকে প্রতিফলিত করে। পাঠযোগ্যতা গুরুত্বপূর্ণ, অপটিমাইজারের জন্যও।

তাত্ত্বিকভাবে কমপক্ষে এবং যদি বাস্তবে না হয় তবে কয়েক বছরের বাস্তবতায় - দ্রুততম কোড তৈরি করতে কোনও অপ্টিমাইজারের পক্ষে এটি সেরা পূর্বশর্ত।

পরামিতি পাসের প্রসঙ্গে পারফরম্যান্স বিবেচনার বিষয়টি প্রায়শই ওভাররেটেড হয়। পারফেক্ট ফরওয়ার্ডিং এর একটি উদাহরণ। এর মতো emplace_backক্রিয়াকলাপগুলি বেশিরভাগই খুব স্বল্প এবং যাহাই হউক না কেন।

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