কেন একটি ল্যাম্বডায় 1 বাইটের আকার থাকে?


90

আমি সি ++ তে কিছু ল্যাম্বডাসের স্মৃতি নিয়ে কাজ করছি, তবে আমি তাদের আকারটি দেখে কিছুটা হতবাক হয়েছি।

আমার পরীক্ষার কোডটি এখানে:

#include <iostream>
#include <string>

int main()
{
  auto f = [](){ return 17; };
  std::cout << f() << std::endl;
  std::cout << &f << std::endl;
  std::cout << sizeof(f) << std::endl;
}

আপনি এটি এখানে চালাতে পারেন: http://fiddle.jyt.io/github/b13f682d1237eb69ebdc60728bb52598

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

17
0x7d90ba8f626f
1

এটি পরামর্শ দেয় যে আমার ল্যাম্বদার আকার 1।

  • এটা কিভাবে সম্ভব?

  • লাম্বদা কি কমপক্ষে, এর প্রয়োগের জন্য একটি পয়েন্টার হওয়া উচিত নয়?


17
এটি একটি ফাংশন অবজেক্ট (একটি structসহ operator()) হিসাবে বাস্তবায়িত হয়েছে
জর্জি_পিটার

14
এবং একটি খালি কাঠামো 0 আকার হতে পারে না তাই 1 ফলাফল। কিছু ক্যাপচার করার চেষ্টা করুন এবং দেখুন আকারে কী ঘটে।
মোহামাদ এলঘাওয়াই

4
কেন একটি ল্যাম্বডা পয়েন্টার হওয়া উচিত ??? এটি এমন একটি অবজেক্ট যার কল অপারেটর রয়েছে।
কেরেক এসবি 27'16

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

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

উত্তর:


108

প্রশ্নে থাকা ল্যাম্বদার আসলে কোনও রাজ্য নেই

পরীক্ষা করা:

struct lambda {
  auto operator()() const { return 17; }
};

এবং যদি আমাদের ছিল lambda f;, এটি একটি খালি শ্রেণি। উপরেরগুলি কেবল lambdaআপনার ল্যাম্বদার মতোই নয়, এটি (মূলত) আপনার ল্যাম্বদা কীভাবে প্রয়োগ করা হয়! (এটি পয়েন্টার অপারেটর কাজ করতে একটি অন্তর্নিহিত কাস্টেরও প্রয়োজন, এবং নামটি lambdaকয়েকটি সংকলক-উত্পাদিত সিউডো-গাইডের সাথে প্রতিস্থাপিত হতে চলেছে)

সি ++ এ, অবজেক্টগুলি পয়েন্টার নয়। তারা প্রকৃত জিনিস। তারা কেবলমাত্র তাদের মধ্যে ডেটা সংরক্ষণের জন্য প্রয়োজনীয় স্থানটি ব্যবহার করে। একটি বস্তুর পয়েন্টার একটি বস্তুর চেয়ে বড় হতে পারে।

আপনি যদি কোনও ল্যাম্বডাকে কোনও ফাংশনের পয়েন্টার হিসাবে মনে করতে পারেন, তবে তা নয়। আপনি auto f = [](){ return 17; };কোনও আলাদা ফাংশন বা ল্যাম্বডায় পুনরায় সাইন করতে পারবেন না !

 auto f = [](){ return 17; };
 f = [](){ return -42; };

উপরে অবৈধ । কোন জায়গা নেই fস্টোরে যা ফাংশন বলা হতে যাচ্ছে - যে তথ্য সংরক্ষণ করা হয় টাইপ এর f, মূল্য না f!

আপনি যদি এটি করেন:

int(*f)() = [](){ return 17; };

অথবা এটা:

std::function<int()> f = [](){ return 17; };

আপনি আর সরাসরি ল্যাম্বডা সংরক্ষণ করছেন না। এই উভয় ক্ষেত্রেই f = [](){ return -42; }আইনী - সুতরাং এই ক্ষেত্রে আমরা কোন ফাংশনটির মান বিবেচনা করছি তা সংরক্ষণ করছি f। এবং sizeof(f)এটি আর নয় 1, বরং sizeof(int(*)())বড় বা বৃহত্তর (মূলত, পয়েন্টার আকারের বা বৃহত্তর হবেন, যেমনটি আপনি প্রত্যাশা করেন std::functionএকটি ন্যূনতম মাপটি মান দ্বারা সূচিত হয় (তারা একটি নির্দিষ্ট আকার পর্যন্ত "নিজের ভিতরে" কল করতে সক্ষম হন)) যা অনুশীলনে ফাংশন পয়েন্টার হিসাবে অন্তত বৃহত্তর)।

সেক্ষেত্রে int(*f)()আপনি কোনও ফাংশন পয়েন্টার এমন কোনও ফাংশনে সংরক্ষণ করছেন যা যদি সেই ল্যাম্বডাকে ডাকে। এটি কেবল স্টেটলেস ল্যাম্বডাসের জন্য কাজ করে (খালি []ক্যাপচার তালিকার সাথে রয়েছে)।

সেক্ষেত্রে std::function<int()> fআপনি একটি টাইপ-ইরেজরের ক্লাসের std::function<int()>উদাহরণ তৈরি করছেন যে (এই ক্ষেত্রে) একটি অভ্যন্তরীণ বাফারে আকার -1 ল্যাম্বদার একটি অনুলিপি সংরক্ষণ করতে নতুন স্থান ব্যবহার করে (এবং, যদি একটি বৃহত্তর ল্যাম্বদা পাস করা হয় (আরও রাষ্ট্রের সাথে) ), হিপ বরাদ্দ ব্যবহার করবে)।

অনুমান হিসাবে, এই জাতীয় কিছু সম্ভবত আপনার মনে হয় যা চলছে। যে একটি ল্যাম্বদা এমন একটি বস্তু যার প্রকারের স্বাক্ষর দ্বারা বর্ণনা করা হয়। C ++, এটা lambdas করতে সিদ্ধান্ত নেওয়া হয় শূন্য খরচ ম্যানুয়াল ফাংশন বস্তুর বাস্তবায়ন উপর বিমূর্ত। এটি আপনাকে একটি ল্যাম্বডাকে একটি stdঅ্যালগরিদম (বা অনুরূপ) এ পাস করতে দেয় এবং যখন এটি অ্যালগরিদম টেম্পলেটটি ইনস্ট্যান্ট করে তখন এর সামগ্রীগুলি সংকলকটিতে পুরোপুরি দৃশ্যমান হয়। যদি কোনও ল্যাম্বদার মতো ধরণের থাকে তবে std::function<void(int)>এর সামগ্রীগুলি সম্পূর্ণরূপে দৃশ্যমান হবে না এবং একটি হস্ত-কারুকৃত ফাংশন অবজেক্টটি দ্রুত হতে পারে।

সি ++ মানককরণের লক্ষ্য হ'ল ক্র্যাফ্টেড সি কোডের ওপরে শূন্য ওভারহেড সহ উচ্চ স্তরের প্রোগ্রামিং।

এখন যেহেতু আপনি বুঝতে পেরেছেন যে fবাস্তবে আপনি রাষ্ট্রহীন, আপনার মাথায় আরও একটি প্রশ্ন থাকা উচিত: ল্যাম্বডারের কোনও রাজ্য নেই। কেন এটি আকার হয় না 0?


সংক্ষিপ্ত উত্তর আছে।

সি ++ এর সমস্ত বস্তুর মানের নীচে ন্যূনতম আকার 1 থাকতে হবে এবং একই ধরণের দুটি অবজেক্টের একই ঠিকানা থাকতে পারে না। এগুলি সংযুক্ত, কারণ ধরণের অ্যারেতে Tউপাদানগুলি sizeof(T)পৃথক পৃথক করে রাখা হবে ।

এখন এটির কোনও রাজ্য না থাকায় এটি কখনও কখনও স্থান গ্রহণ করতে পারে না। এটি "একা" থাকলে এটি ঘটতে পারে না, তবে কিছু প্রসঙ্গে এটি ঘটতে পারে। std::tupleএবং অনুরূপ লাইব্রেরি কোড এই সত্যটিকে কাজে লাগায়। এটা যেভাবে কাজ করে:

যেহেতু একটি ল্যাম্বডা operator()অতিরিক্ত লোডযুক্ত শ্রেণীর সমতুল্য , স্টেটলেস ল্যাম্বডাস ( []ক্যাপচার তালিকার সাথে) সমস্ত খালি ক্লাস। তারা আছে sizeofএর 1। প্রকৃতপক্ষে, আপনি যদি তাদের কাছ থেকে উত্তরাধিকারী হন (যা অনুমোদিত!) তবে তারা এতক্ষণ কোনও স্থান গ্রহণ করবে না কারণ এটি কোনও একই ধরণের ঠিকানার সংঘর্ষের কারণ না করে । (এটি খালি বেস অপ্টিমাইজেশন হিসাবে পরিচিত)।

template<class T>
struct toy:T {
  toy(toy const&)=default;
  toy(toy &&)=default;
  toy(T const&t):T(t) {}
  toy(T &&t):T(std::move(t)) {}
  int state = 0;
};

template<class Lambda>
toy<Lambda> make_toy( Lambda const& l ) { return {l}; }

এটি sizeof(make_toy( []{std::cout << "hello world!\n"; } ))হ'ল sizeof(int)(ভাল, উপরেরটি অবৈধ কারণ আপনি মূল্যায়ন-না-করা প্রেক্ষাপটে ল্যাম্বদা তৈরি করতে পারবেন না: আপনাকে একটি নাম তৈরি করতে হবে auto toy = make_toy(blah);তবে করুক sizeof(blah), তবে এটি কেবল গোলমাল)। sizeof([]{std::cout << "hello world!\n"; })এখনও 1(অনুরূপ যোগ্যতা) হয়।

যদি আমরা অন্য খেলনা টাইপ তৈরি করি:

template<class T>
struct toy2:T {
  toy2(toy2 const&)=default;
  toy2(T const&t):T(t), t2(t) {}
  T t2;
};
template<class Lambda>
toy2<Lambda> make_toy2( Lambda const& l ) { return {l}; }

এই ল্যাম্বডা দুটি কপি আছে । তারা একই ঠিকানা ভাগ করতে পারে না, sizeof(toy2(some_lambda))হয় 2!


6
নীট: একটি ফাংশন পয়েন্টার একটি শূন্য * এর চেয়ে ছোট হতে পারে। দুটি historicalতিহাসিক উদাহরণ: প্রথমত সম্বোধিত মেশিনগুলি যেখানে মাপের (অকার্যকর *) == আকারের (চর *)> আকারের (স্ট্রাক *) == আকারের (অভ্যন্তরীণ *)। (শূন্য * এবং চর * টি একটি শব্দের মধ্যে অফসেট ধরে রাখতে কিছু অতিরিক্ত বিট প্রয়োজন) .সইভাবে 8086 মেমরি মডেল যেখানে শূন্য * / ইন্ট * সেগমেন্ট + অফসেট ছিল এবং সমস্ত স্মৃতি কভার করতে পারে তবে একক 64৪ কে বিভাগে ফাংশন লাগানো হয়েছে ( সুতরাং একটি ফাংশন পয়েন্টার ছিল মাত্র 16 বিট)।
মার্টিন Bonner মনিকা

4
@ মার্টিন সত্য অতিরিক্ত ()যুক্ত হয়েছে।
ইয়াক্ক - অ্যাডাম নেভ্রামুমন্ট

50

একটি ল্যাম্বদা কোনও ফাংশন পয়েন্টার নয়।

একটি ল্যাম্বদা একটি শ্রেণীর উদাহরণ। আপনার কোডটি প্রায় সমান:

class f_lambda {
public:

  auto operator() { return 17; }
};

f_lambda f;
std::cout << f() << std::endl;
std::cout << &f << std::endl;
std::cout << sizeof(f) << std::endl;

একটি ল্যাম্বডাকে উপস্থাপন করে এমন অভ্যন্তরীণ শ্রেণীর কোনও শ্রেণীর সদস্য নেই, সুতরাং sizeof()এটি 1 (এটি 0 হতে পারে না, অন্য কোথাও পর্যাপ্ত পর্যায়ে বর্ণিত কারণে )।

যদি আপনার ল্যাম্বদা কিছু ভেরিয়েবল ক্যাপচার করে থাকে তবে সেগুলি শ্রেণীর সদস্যদের সমতুল্য sizeof()হবে এবং সেই অনুযায়ী আপনার ইঙ্গিত দেবে।


4
আপনি কি "অন্য কোথাও" এর সাথে লিঙ্ক করতে sizeof()পারেন , যা কেন 0 টি হতে পারে না তা ব্যাখ্যা করে ?
ব্যবহারকারী 1717828

26

আপনার সংকলক কমবেশি ল্যাম্বডাকে নিম্নলিখিত স্ট্রাক টাইপের সাথে অনুবাদ করে:

struct _SomeInternalName {
    int operator()() { return 17; }
};

int main()
{
     _SomeInternalName f;
     std::cout << f() << std::endl;
}

যেহেতু সেই স্ট্রাক্টের কোনও অ-স্থায়ী সদস্য নেই, এটির খালি কাঠামোর মতো আকার রয়েছে, যা 1

আপনি আপনার ল্যাম্বডায় একটি খালি খালি ক্যাপচার তালিকা যুক্ত করার সাথে সাথেই এটি পরিবর্তিত হবে:

int i = 42;
auto f = [i]() { return i; };

যা অনুবাদ করবে

struct _SomeInternalName {
    int i;
    _SomeInternalName(int outer_i) : i(outer_i) {}
    int operator()() { return i; }
};


int main()
{
     int i = 42;
     _SomeInternalName f(i);
     std::cout << f() << std::endl;
}

যেহেতু উত্পাদিত স্ট্রাক্ট এখন intক্যাপচারের জন্য একটি অ স্থিতিশীল সদস্য সঞ্চয় করতে হবে, এর আকার বাড়বে sizeof(int)। আপনি আরও স্টাফ ক্যাপচার করার সাথে সাথে আকারটি বাড়তে থাকবে।

(দয়া করে কাঠের সাদৃশ্য লবণের সাথে দান করুন la ল্যাম্বডাস অভ্যন্তরীণভাবে কীভাবে কাজ করে সে সম্পর্কে যুক্তিযুক্ত কারণ এটি একটি সহজ উপায়, তবে সংকলকটি কী করবে তার এটি আক্ষরিক অনুবাদ নয়)


12

ল্যাম্বডাটি কি মিমিমামে হওয়া উচিত নয়, এটির প্রয়োগের জন্য একটি নির্দেশক?

অগত্যা। মান অনুসারে, অনন্য, নামবিহীন শ্রেণীর আকার বাস্তবায়ন-সংজ্ঞায়িত । থেকে উদ্ধৃতাংশ [expr.prim.lambda] , সি ++ 14 (জোর খনি):

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

[...]

একটি বাস্তবায়ন ভিন্নভাবে কি নিচে আলোচনা করা হয়েছে থেকে অবসান টাইপ সংজ্ঞায়িত হতে পারে এই প্রোগ্রাম পর্যবেক্ষণযোগ্য আচরণ পরিবর্তন না দেওয়া পরিবর্তন করে ছাড়া অন্য :

- ক্লোজার ধরণের আকার এবং / অথবা প্রান্তিককরণ ,

- বন্ধের ধরণটি তুচ্ছভাবে অনুলিপিযোগ্য কিনা (ক্লজ 9),

- বন্ধের ধরণটি একটি স্ট্যান্ডার্ড-লেআউট শ্রেণি কিনা (ক্লজ 9), বা

- ক্লোজার টাইপটি কোনও পিওডি ক্লাস কিনা (ক্লজ 9)

আপনার ক্ষেত্রে - আপনি যে সংকলকটি ব্যবহার করেন তার জন্য - আপনি একটি আকার পান যার অর্থ এটি স্থির নয়। এটি বিভিন্ন সংকলক প্রয়োগের মধ্যে পৃথক হতে পারে।


আপনি কি নিশ্চিত যে এই বিটটি প্রযোজ্য? ক্যাপচার-গ্রুপবিহীন ল্যাম্বডা আসলে "ক্লোজার" নয়। (মানকটি খালি ক্যাপচার-গ্রুপ ল্যাম্বডাসকে যাইহোক "বন্ধ" হিসাবে উল্লেখ করে?)
কাইল স্ট্র্যান্ড

4
হ্যাঁ এটা করে. এই স্ট্যান্ডার্ডটি বলে " ল্যাম্বডা-এক্সপ্রেশনের মূল্যায়নের ফলে একটি অস্থায়ী অস্থায়ী ফলাফল আসে This এই অস্থায়ীটিকে ক্লোজার অবজেক্ট বলে " "
কিংবদন্তি 2 কে

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

7

Http://en.cppreferences.com/w/cpp/language/lambda থেকে :

ল্যাম্বডা এক্সপ্রেশনটি অনন্য নামহীন নন-ইউনিয়ন নন-সমষ্টিগত শ্রেণীর প্রকারের একটি নামহীন সাময়িক অবজেক্ট তৈরি করে, ক্লোজার টাইপ হিসাবে পরিচিত , যা সর্বনিম্ন ব্লক স্কোপ, শ্রেণি স্কোপ বা নেমস্পেসের স্কোপগুলিতে অন্তর্ভুক্ত থাকে (ADL এর উদ্দেশ্যে) ঘোষিত হয় contains ল্যাম্বডা এক্সপ্রেশন।

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

রেফারেন্স দ্বারা ক্যাপচার করা সত্তাগুলির জন্য (ডিফল্ট ক্যাপচারের সাথে [&] বা চরিত্রটি ব্যবহার করার সময় & যেমন, [& a, & b, & c]), অতিরিক্ত তথ্য সদস্যদের বন্ধের ধরণে ঘোষণা করা থাকলে এটি অনির্দিষ্ট নয়

Http://en.cppreferences.com/w/cpp/language/sizeof থেকে

খালি শ্রেণির ধরণের ক্ষেত্রে প্রয়োগ করা হলে সর্বদা 1 প্রদান করে।

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