কনস্ট-রেফারেন্স দিয়ে আমার কোনও স্ট্যান্ড :: ফাংশন পাস করা উচিত?


141

ধরা যাক আমার একটি ফাংশন আছে যা একটি নেয় std::function:

void callFunction(std::function<void()> x)
{
    x();
}

xপরিবর্তে আমি কি কনস্ট্যান্ড-রেফারেন্স দিয়ে পাস করব ?:

void callFunction(const std::function<void()>& x)
{
    x();
}

এই প্রশ্নের উত্তর কী কার্যকারিতা এটি দিয়ে কাজ করে তার উপর নির্ভর করে কি পরিবর্তন করে? উদাহরণস্বরূপ যদি এটি কোনও শ্রেণীর সদস্য ফাংশন বা কনস্ট্রাক্টর থাকে যা std::functionমেম্বার ভেরিয়েবলের মধ্যে সঞ্চয় করে বা আরম্ভ করে ।


1
সম্ভবত না. আমি নিশ্চিতভাবে জানি না, তবে আমি এর sizeof(std::function)চেয়ে বেশি আর কিছু হবে না বলে আশা করব 2 * sizeof(size_t), যা আপনি কোনও দৃ const় রেফারেন্সের জন্য বিবেচনা করবেন এমন ন্যূনতম আকার।
ম্যাটস পিটারসন

12
@ ম্যাটস: আমি মনে করি না যে std::functionমোড়কের আকারটি অনুলিপি করার জটিলতার মতো গুরুত্বপূর্ণ। যদি গভীর অনুলিপিগুলি জড়িত থাকে তবে এটি sizeofপ্রস্তাবগুলির চেয়ে অনেক বেশি ব্যয়বহুল হতে পারে ।
বেন ভয়েগট


operator()()হয় constতাই একটি const রেফারেন্স কাজ করা উচিত। তবে আমি কখনই স্ট্যান্ড :: ফাংশন ব্যবহার করি নি।
নীল বসু

@ ইয়াক্ক আমি কেবলমাত্র একটি ল্যাম্বডা সরাসরি ফাংশনে পৌঁছেছি।
সোভেন অ্যাডব্রিং

উত্তর:


79

আপনি যদি পারফরম্যান্স চান তবে মান দিয়ে স্টোর করে রাখুন।

ধরুন আপনার কাছে "ইউআই থ্রেডে এটি চালান" নামে একটি ফাংশন রয়েছে।

std::future<void> run_in_ui_thread( std::function<void()> )

যা "ui" থ্রেডে কিছু কোড চালায়, তারপরে এটি futureসম্পন্ন হওয়ার সংকেত দেয় । (ইউআই ফ্রেমওয়ার্কগুলিতে দরকারী যেখানে ইউআই থ্রেড যেখানে আপনার ইউআই উপাদানগুলির সাথে গোলযোগ হওয়ার কথা)

আমাদের দুটি স্বাক্ষর রয়েছে যা আমরা বিবেচনা করছি:

std::future<void> run_in_ui_thread( std::function<void()> ) // (A)
std::future<void> run_in_ui_thread( std::function<void()> const& ) // (B)

এখন, আমরা নিম্নলিখিত হিসাবে এটি ব্যবহার করতে পারে:

run_in_ui_thread( [=]{
  // code goes here
} ).wait();

যা কোনও বেনামে বন্ধ (একটি ল্যাম্বডা) তৈরি করবে, এর মধ্যে একটি তৈরি std::functionকরবে, এটি run_in_ui_threadফাংশনে পাস করবে , তারপরে এটি মূল থ্রেডে চলমান শেষ হওয়ার জন্য অপেক্ষা করুন।

ক্ষেত্রে (এ), std::functionআমাদের ল্যাম্বদা থেকে সরাসরি নির্মিত হয়, যা পরে এর মধ্যে ব্যবহার করা হয় run_in_ui_thread। ল্যাম্বদাটি moveডি তে প্রবেশ করানো হয়েছে std::function, সুতরাং যে কোনও চলনীয় স্থিতি দক্ষতার সাথে এটিতে চালিত হয়।

দ্বিতীয় ক্ষেত্রে, একটি অস্থায়ী std::functionতৈরি করা হয়, ল্যাম্বড্ডা এতে moveপ্রবেশ করা হয়, তারপরে সেই অস্থায়ীটি std::functionরেফারেন্সের মধ্যে ব্যবহার করা হয় run_in_ui_thread

এতক্ষণ, এত ভাল - তাদের দু'জন একইরকম পারফর্ম করে। বাদে run_in_ui_threadতার ক্রিয়াকলাপের যুক্তির একটি অনুলিপি তৈরি করতে চলেছে ইউআই থ্রেডে চালানোর জন্য! (এটি সম্পন্ন হওয়ার আগে এটি ফিরে আসবে, সুতরাং এটি কেবল এটির কোনও উল্লেখ ব্যবহার করতে পারে না)। কেস জন্য (ক), কেবলমাত্র আমরা তার দীর্ঘমেয়াদী স্টোরেজ মধ্যে। ক্ষেত্রে (খ), আমরা নকল করতে বাধ্য হয় ।movestd::functionstd::function

এই স্টোরটি মান দ্বারা উত্তরণকে আরও অনুকূল করে তোলে। যদি কোনও সম্ভাবনা থাকে তবে আপনি একটি অনুলিপি সংরক্ষণ করছেন std::function, মান দিয়ে পাস করুন। অন্যথায়, যে কোনও উপায়ে মোটামুটি সমতুল্য: বাই-ভ্যালুর একমাত্র নেতিবাচকতা হ'ল যদি আপনি একই ভারী ভারী গ্রহণ করে থাকেন std::functionএবং একের পর এক উপ-পদ্ধতি ব্যবহার করেন। তা বাদ দিলে একটি ক moveহিসাবে দক্ষ হবে const&

এখন, দু'জনের মধ্যে আরও কিছু পার্থক্য রয়েছে যা বেশিরভাগ ক্ষেত্রে কিক হয় যদি আমাদের মধ্যে স্থির অবস্থা থাকে std::function

ধরে নিন যে স্টোরগুলিতে std::functionএকটি কিছু দিয়ে কিছু বস্তু রয়েছে operator() constতবে এতে কিছু mutableডেটা সদস্য রয়েছে যা এটি সংশোধন করে (কতটা অভদ্র!)।

ইন std::function<> const&মামলা, mutableসংশোধিত যেটা সদস্যদের ফাংশন কল আউট সঞ্চারিত হবে। ইন std::function<>মামলা, তারা না হয়।

এটি তুলনামূলকভাবে অদ্ভুত কোণার কেস।

আপনি std::functionঅন্য কোনও সম্ভাব্য ভারী ওজন, সস্তায় চলমান প্রকারের মতো আচরণ করতে চান । চলাচল সস্তা, অনুলিপি ব্যয়বহুল হতে পারে।


"আপনি যদি স্টোর করে থাকেন তবে মান দিয়ে পাস করুন" এর অর্থগত সুবিধাটি যেমন আপনি বলেছিলেন যে চুক্তির মাধ্যমে ফাংশনটি আর্গুমেন্টের পাসওয়ার্ডটি আটকে রাখতে পারে না। তবে এটি কি সত্য যে "এই ব্যতীত কোনও পদক্ষেপ কনস্টের মত দক্ষ হবে" এবং "? আমি সর্বদা একটি অনুলিপি অপারেশনের ব্যয় এবং মুভ অপারেশনের ব্যয় দেখতে পাই। পাশ কাটিয়ে const&আমি কেবল অনুলিপি অপারেশনটির ব্যয় দেখতে পাচ্ছি।
ceztko

2
@ceztko (এ) এবং (বি) উভয় ক্ষেত্রে std::functionল্যাম্বডা থেকে অস্থায়ী তৈরি করা হয়। (এ) এ, অস্থায়ীটি যুক্তির সাথে যুক্ত হয় run_in_ui_thread। (খ) তে অস্থায়ীটির নিকট একটি রেফারেন্স দেওয়া হয়েছে run_in_ui_thread। যতক্ষণ আপনার std::functions ল্যাম্বডাস থেকে অস্থায়ী হিসাবে তৈরি করা হয়, সেই ধারাটি ধারণ করে। পূর্ববর্তী অনুচ্ছেদটি যেখানে std::functionস্থির থাকে সেই ক্ষেত্রেই কাজ করে । যদি আমরা সংরক্ষণ না করে থাকি , কেবল একটি ল্যাম্বডা থেকে তৈরি করি function const&এবং functionঠিক একই ওভারহেড থাকি ।
ইয়াক্ক - অ্যাডাম নেভ্রামাউন্ট

আহ আমি দেখি! এটি অবশ্যই বাইরে যা ঘটে তার উপর নির্ভর করে run_in_ui_thread()। "রেফারেন্স দ্বারা পাস করুন, তবে আমি ঠিকানাটি সংরক্ষণ করব না" বলার জন্য কি কেবল স্বাক্ষর আছে?
ceztko


1
@ ইয়াক্ক-অ্যাডাম নেভ্রামুমন্ট যদি রেয়্যালু রেফার পাশ দিয়ে যাওয়ার জন্য অন্য বিকল্পটি কভার করার জন্য আরও সম্পূর্ণ হয়ে থাকে:std::future<void> run_in_ui_thread( std::function<void()>&& )
পাভেল পি

33

আপনি যদি পারফরম্যান্স সম্পর্কে চিন্তিত হন এবং আপনি ভার্চুয়াল সদস্য ফাংশনটি সংজ্ঞায়িত করছেন না, তবে সম্ভবত আপনি সম্ভবত ব্যবহার করবেন না std::function

ফান্টরটি টাইপ করা একটি টেম্পলেট প্যারামিটার std::functionথেকে ফান্টারের যুক্তি অন্তর্ভুক্ত করার চেয়ে আরও বেশি অনুকূলকরণের অনুমতি দেয় । এই অপ্টিমাইজেশনের প্রভাব কীভাবে পাস হবে সে সম্পর্কে অনুলিপি-বনাম-ইন্ডিরেশনের উদ্বেগকে ছাড়িয়ে যাওয়ার সম্ভাবনা রয়েছে std::function

দ্রুত:

template<typename Functor>
void callFunction(Functor&& x)
{
    x();
}

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

13
@ বেন: আমি মনে করি এটি প্রয়োগের সবচেয়ে আধুনিক হিপ্পি-বান্ধব উপায় হ'ল std::forward<Functor>(x)();ফ্যান্টারের মান বিভাগটি সংরক্ষণ করা, কারণ এটি একটি "সর্বজনীন" রেফারেন্স। যদিও 99% ক্ষেত্রে কোনও পার্থক্য করা যাচ্ছে না।
GManNGG

1
@ বেন ভয়েগ্ট আপনার মামলার জন্য, আমি কি কোনও পদক্ষেপ নিয়ে ফাংশনটি কল করব? callFunction(std::move(myFunctor));
আরিয়াস_জেসি

2
@ আরিয়াস_জেসি: প্যারামিটারটি যদি ল্যাম্বদা হয় তবে এটি ইতিমধ্যে একটি মূল্যবান। আপনার যদি লভ্যালু std::moveথাকে তবে আপনি অন্য কোনও উপায়ে আর প্রয়োজন না হলে আপনি ব্যবহার করতে পারেন বা আপনি যদি বিদ্যমান অবজেক্টের বাইরে যেতে চান না তবে সরাসরি পাস করুন। রেফারেন্স-ভেঙে দেওয়া নিয়মগুলি নিশ্চিত করে যে callFunction<T&>()এটির কোনও পরামিতি রয়েছে T&, না T&&
বেন ভয়েগট

1
@ বল্টজম্যান ব্রেন: আমি এই পরিবর্তনটি না করা বেছে নিয়েছি কারণ এটি কেবলমাত্র সহজতম ক্ষেত্রে, যখন ফাংশনটি কেবল একবার ডাকা হয়। আমার উত্তর "আমি কীভাবে কোনও ফাংশন অবজেক্টটি পাস করব?" এই প্রশ্নের উত্তর এবং এমন কোনও ফাংশনে সীমাবদ্ধ নয় যা নিঃশর্তভাবে সেই ফান্টারে ঠিক একবার ডাকে।
বেন ভয়েগট

25

সি ++ 11 এ যথারীতি মান / রেফারেন্স / কনস্ট্যান্ট-রেফারেন্স দিয়ে পাস করা আপনার যুক্তির সাথে কী করবেন তার উপর নির্ভর করে। std::functionএর থেকেও আলাদা নয়।

মান দ্বারা পাস করা আপনাকে যুক্তিটি ভেরিয়েবলে স্থানান্তরিত করতে দেয় (সাধারণত কোনও শ্রেণীর সদস্য ভেরিয়েবল):

struct Foo {
    Foo(Object o) : m_o(std::move(o)) {}

    Object m_o;
};

যখন আপনি জানেন যে আপনার ফাংশনটি তার যুক্তি সরিয়ে ফেলবে, এটি সর্বোত্তম সমাধান, এইভাবে আপনার ব্যবহারকারীরা কীভাবে আপনার ফাংশনটিকে কল করবেন তা নিয়ন্ত্রণ করতে পারে:

Foo f1{Object()};               // move the temporary, followed by a move in the constructor
Foo f2{some_object};            // copy the object, followed by a move in the constructor
Foo f3{std::move(some_object)}; // move the object, followed by a move in the constructor

আমি বিশ্বাস করি আপনি ইতিমধ্যে (নন) কনস্ট-রেফারেন্সগুলির শব্দার্থবিদ্যা জানেন তাই আমি বিন্দুটি বেলব না। আপনার যদি আমার এই সম্পর্কে আরও ব্যাখ্যা যুক্ত করার প্রয়োজন হয় তবে কেবল জিজ্ঞাসা করুন এবং আমি আপডেট করব।

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