কেন একটি টি * রেজিস্টারে পাস করা যেতে পারে, তবে একটি অনন্য_পাত্র <টি> পারে না?


85

আমি সিপিসকন 2019 এ চ্যানডলার ক্যারথের বক্তব্যটি দেখছি:

কোনও জিরো-কাস্টম বিমূর্তি নেই

এতে তিনি একটি উদাহরণ দিয়েছিলেন যে std::unique_ptr<int>ওভার ওভার ব্যবহার করে আপনি কতটা ওভারহেড ব্যয় করেছেন তাতে তিনি কীভাবে অবাক হয়েছিলেন int*; সেগমেন্টটি সময় পয়েন্ট 17:25 এর প্রায় শুরু হয়।

আপনি তাঁর উদাহরণ-জুটি-স্নিপেটস (গডবোল্ট.অর্গ) এর সংকলনের ফলাফলগুলি একবার দেখে নিতে পারেন - সাক্ষ্য দিতে যে, সত্যই মনে হয় যে সংকলকটি অনন্য_পিটার মানটি পাস করতে রাজি নয় - যা নীচের লাইনে আসলে কেবলমাত্র একটি ঠিকানা - একটি রেজিস্টারের অভ্যন্তরে, কেবল সরাসরি স্মৃতিতে।

মিঃ ক্যারুথ প্রায় 27:00 টার দিকে একটি পয়েন্টটি উল্লেখ করেন যে সি ++ এবিআই -কে স্মৃতিতে পাস করার জন্য উপ-মান প্যারামিটারগুলি (কিছু কিন্তু সমস্ত নয়; সম্ভবত - অ-আদিম ধরণের? অ-তুচ্ছ-গঠনমূলক ধরনের?) প্রয়োজন হয় requires পরিবর্তে একটি রেজিস্টার মধ্যে।

আমার প্রশ্নগুলো:

  1. এটি কি আসলে কিছু প্ল্যাটফর্মে এবিআইয়ের প্রয়োজনীয়তা? (যা?) অথবা সম্ভবত এটি কিছু পরিস্থিতিতে কিছুটা হতাশার?
  2. এবিআই কেন এমন হয়? অর্থাত্ যদি কোনও কাঠামো / শ্রেণির ক্ষেত্রগুলি যদি নিবন্ধগুলির মধ্যে ফিট হয়, বা এমনকি একটি একক নিবন্ধক - তবে কেন আমরা এই রেজিস্টারে এটি পাস করতে সক্ষম হব না?
  3. সি ++ স্ট্যান্ডার্ড কমিটি সাম্প্রতিক বছরগুলিতে, বা কখনও এই বিষয়টি নিয়ে আলোচনা করেছে?

পিএস - যাতে কোনও কোড ছাড়াই এই প্রশ্নটি না ফেলে:

সরল পয়েন্টার:

void bar(int* ptr) noexcept;
void baz(int* ptr) noexcept;

void foo(int* ptr) noexcept {
    if (*ptr > 42) {
        bar(ptr); 
        *ptr = 42; 
    }
    baz(ptr);
}

অনন্য পয়েন্টার:

using std::unique_ptr;
void bar(int* ptr) noexcept;
void baz(unique_ptr<int> ptr) noexcept;

void foo(unique_ptr<int> ptr) noexcept {
    if (*ptr > 42) { 
        bar(ptr.get());
        *ptr = 42; 
    }
    baz(std::move(ptr));
}


6
যদি আমার অনুমান করতে হয় তবে আমি বলব এটি একটি তুচ্ছ সদস্যের ফাংশনগুলির সাথে একটি thisপয়েন্টারের প্রয়োজন যা একটি বৈধ স্থানে নির্দেশ করে। unique_ptrতাদের আছে। এই উদ্দেশ্যে রেজিস্টার স্পিলিং পুরো "একটি রেজিস্টার পাস" অপ্টিমাইজেশন অস্বীকার করা হবে।
গল্পগ্রাহক - আনস্ল্যান্ডার মনিকা

2
itanium-cxx-abi.github.io/cxx-abi/abi.html# কল । সুতরাং এই আচরণ প্রয়োজন। কেন? itanium-cxx-abi.github.io/cxx-abi/cxx-closed.html , সি -7 ইস্যুটি অনুসন্ধান করুন। সেখানে কিছু ব্যাখ্যা আছে, তবে এটি খুব বিস্তারিত নয়। তবে হ্যাঁ, এই আচরণটি আমার কাছে যৌক্তিক বলে মনে হয় না। এই বস্তুগুলি সাধারণত স্ট্যাকের মধ্য দিয়ে যেতে পারে। তাদের স্ট্যাকের দিকে ঠেলাঠেলি করা, এবং তারপরে রেফারেন্সটি পাস করা (কেবল "অ-তুচ্ছ" বিষয়গুলির জন্য) একটি অপব্যয় বলে মনে হচ্ছে।
গেজা

6
দেখে মনে হচ্ছে সি ++ এখানে তার নিজস্ব নীতি লঙ্ঘন করছে, যা বেশ দুঃখজনক। আমি 140% নিশ্চিত হয়েছি যে কোনও অনন্য_পিটার সংকলনের পরে কেবল অদৃশ্য হয়ে যায়। সর্বোপরি এটি কেবল একটি বিলম্বিত ডেস্ট্রাক্টর কল যা সংকলন সময়ে পরিচিত।
ওয়ান ম্যান বানর স্কোয়াড

7
@ ম্যাক্সিম এগারোশকিন: আপনি যদি এটি হাতে লিখে লিখে থাকেন তবে আপনি পয়েন্টারটিকে একটি রেজিস্টারে রাখতেন, তবে স্ট্যাকের উপরে না।
einpoklum

উত্তর:


49
  1. এটি কি আসলে একটি এবিআইয়ের প্রয়োজনীয়তা, বা সম্ভবত এটি কিছু পরিস্থিতিতে কিছুটা হতাশার নয়?

একটি উদাহরণ সিস্টেম ভি অ্যাপ্লিকেশন বাইনারি ইন্টারফেস AMD64 আর্কিটেকচার প্রসেসরের পরিপূরক । এই এবিআইটি 64-বিট x86- সামঞ্জস্যপূর্ণ সিপিইউগুলির জন্য (লিনাক্স x86_64 আর্কিটেকচার)। এটি সোলারিস, লিনাক্স, ফ্রিবিএসডি, ম্যাকস, লিনাক্সের জন্য উইন্ডোজ সাবসিস্টেম অনুসরণ করা হয়:

যদি কোনও সি ++ অবজেক্টের হয় একটি তুচ্ছ নকল কপির কনস্ট্রাক্টর বা একটি তুচ্ছ ত্রুটিযুক্ত ডিস্ট্রাক্টর থাকে তবে এটি অদৃশ্য রেফারেন্স দ্বারা পাস করা হয় (অবজেক্ট প্যারামিটার তালিকায় একটি পয়েন্টার দ্বারা ক্লাস INTEGER রয়েছে)।

একটি তুচ্ছ অনুলিপি কন্সট্রাক্টর বা একটি তুচ্ছ ত্রুটিযুক্ত ডিস্ট্রাক্টর সহ একটি বস্তুর মান দ্বারা পাস করা যায় না কারণ এই জাতীয় অবজেক্টগুলির অবশ্যই ভাল সংজ্ঞাযুক্ত ঠিকানা থাকতে হবে। কোনও ফাংশন থেকে কোনও জিনিস ফেরত দেওয়ার সময় অনুরূপ সমস্যাগুলি প্রযোজ্য।

দ্রষ্টব্য, কেবলমাত্র 2 সাধারণ উদ্দেশ্য রেজিস্টারগুলি একটি তুচ্ছ অনুলিপি নির্মাণকারী এবং একটি তুচ্ছ ডেস্ট্রাক্টরের সাথে 1 অবজেক্ট পাস করার জন্য ব্যবহার করা যেতে পারে, অর্থাত্ sizeof16 টির বেশি নয় এমন বস্তুর কেবল মানগুলি রেজিস্টারে পাস করা যেতে পারে। কলিং কনভেনশনগুলির বিশদ চিকিত্সার জন্য আগ্নার ফগের মাধ্যমে আহ্বান আহ্বানগুলি দেখুন , বিশেষত §7.1 বিষয়বস্তু পাসিং এবং ফেরত। রেজিস্টারে সিমডি প্রকারগুলি পাস করার জন্য পৃথক কলিং কনভেনশন রয়েছে।

অন্যান্য সিপিইউ আর্কিটেকচারের জন্য আলাদা এবিআই রয়েছে।


  1. এবিআই কেন এমন হয়? অর্থাত্ যদি কোনও কাঠামো / শ্রেণির ক্ষেত্রগুলি যদি নিবন্ধগুলির মধ্যে ফিট হয়, বা এমনকি একটি একক নিবন্ধক - তবে কেন আমরা এই রেজিস্টারে এটি পাস করতে সক্ষম হব না?

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

পেডেন্টালি, ডেস্ট্রাক্টররা বস্তুগুলিতে কাজ করে :

কোনও অবজেক্ট তার নির্মাণকালীন [[class.cdtor]) এর জীবদ্দশায় এবং ধ্বংসের সময়কালে স্টোরেজের একটি অঞ্চল দখল করে।

এবং কোনও অবজেক্ট সি ++ এ থাকতে পারে না যদি এর জন্য কোনও ঠিকানাযোগ্য স্টোরেজ বরাদ্দ না করা হয় কারণ বস্তুর পরিচয় এটির ঠিকানা

যখন রেজিস্টারে রাখা তুচ্ছ কপি কনস্ট্রাক্টরের সাথে কোনও সামগ্রীর ঠিকানা প্রয়োজন হয় তখন সংকলকটি কেবলমাত্র বস্তুকে মেমরির মধ্যে সঞ্চয় করতে পারে এবং ঠিকানাটি অর্জন করতে পারে। যদি অনুলিপি নির্মাতা অপ্রয়োজনীয় হয়, অন্যদিকে, সংকলকটি কেবল এটি মেমরির মধ্যে সঞ্চয় করতে পারে না, বরং এটি অনুলিপিটি প্রস্তুতকারী অনুলিপি করতে হবে যা একটি রেফারেন্স নেয় এবং তাই নিবন্ধগুলিতে ঠিকানাটির প্রয়োজন হয়। কলিং কনভেনশন সম্ভবত অনুলিপি করতে পারে না যে অনুলিপিটি অনুলিপি ক্যালিতে ছিল।

এ সম্পর্কে ভাবার আরেকটি উপায়, তুচ্ছভাবে অনুলিপিযোগ্য প্রকারের জন্য কম্পাইলার নিবন্ধগুলিতে কোনও বস্তুর মান স্থানান্তর করে , যা থেকে প্রয়োজনে সরল মেমরি স্টোর দ্বারা কোনও বস্তু পুনরুদ্ধার করা যায়। উদাহরণ:

void f(long*);
void g(long a) { f(&a); }

সিস্টেম ভি এবিআই সহ x86_64-এ সংকলন করা হয়েছে:

g(long):                             // Argument a is in rdi.
        push    rax                  // Align stack, faster sub rsp, 8.
        mov     qword ptr [rsp], rdi // Store the value of a in rdi into the stack to create an object.
        mov     rdi, rsp             // Load the address of the object on the stack into rdi.
        call    f(long*)             // Call f with the address in rdi.
        pop     rax                  // Faster add rsp, 8.
        ret                          // The destructor of the stack object is trivial, no code to emit.

তাঁর চিন্তা-চেতনামূলক আলোচনায় চ্যানডলার ক্যারুথ উল্লেখ করেছেন যে ধ্বংসাত্মক পদক্ষেপের বাস্তবায়নের জন্য অ্যাবিআইয়ের একটি ব্রেকিং প্রয়োজন হতে পারে (যা অন্যান্য জিনিসের মধ্যেও) যা উন্নতি করতে পারে। আইএমও, নতুন এবিআই ব্যবহার করে ফাংশনগুলি স্পষ্টভাবে একটি নতুন ভিন্ন সংযোগ স্থাপন করতে পছন্দ করে, আইবিও পরিবর্তনটি অবিচ্ছিন্ন হতে পারে, উদাহরণস্বরূপ এগুলি extern "C++20" {}ব্লকে ঘোষণা করুন (সম্ভবত, বিদ্যমান এপিআইগুলিকে স্থানান্তর করার জন্য একটি নতুন ইনলাইন নেমস্পেসে)। যাতে নতুন লিঙ্কেজের সাথে নতুন ফাংশন ঘোষণার বিপরীতে সংকলিত কোডই নতুন এবিআই ব্যবহার করতে পারে।

নোট করুন যে যখন ডাকা ফাংশনটি অন্তর্ভুক্ত করা হয়েছে তখন এবিআই আবেদন করে না। পাশাপাশি লিঙ্ক-টাইম কোড প্রজন্মের সাথে সংকলকটি অন্যান্য অনুবাদ ইউনিটে সংজ্ঞায়িত ফাংশনগুলিকে ইনলাইন করতে পারে বা কাস্টম কলিং কনভেনশনগুলি ব্যবহার করতে পারে।


মন্তব্যগুলি বর্ধিত আলোচনার জন্য নয়; এই কথোপকথন চ্যাটে সরানো হয়েছে ।
স্যামুয়েল লিউ

8

সাধারণ এবিআই সহ, অ-তুচ্ছ নষ্টকারী -> নিবন্ধগুলিতে পাস করতে পারে না

(একটি মন্তব্যে @ হ্যারল্ডের উদাহরণ ব্যবহার করে @ ম্যাক্সিমএগরোশকিনের জবাবের একটি পয়েন্টের উদাহরণ; @ ইয়াককের মন্তব্য অনুসারে সংশোধন করা হয়েছে।)

আপনি যদি সংকলন করেন:

struct Foo { int bar; };
Foo test(Foo byval) { return byval; }

তুমি পাও:

test(Foo):
        mov     eax, edi
        ret

অর্থাত্ নিবন্ধটি ( ) এ Fooপাস করা হয় testএবং একটি নিবন্ধে ( edi) এও ফিরে আসে eax

যখন ডেস্ট্রাক্টর তুচ্ছ নয় ( std::unique_ptrযেমন ওপি'র উদাহরণ হিসাবে) - সাধারণ এবিআইগুলির স্ট্যাকের উপরে বসানো দরকার। এটি সত্য যদি এমনকি ডেস্ট্রাক্টর বস্তুর ঠিকানা ব্যবহার না করে।

সুতরাং আপনি যদি কম্পাইল করেন তবে কোনও কিছুই করবেন না এমন চূড়ান্ত ক্ষেত্রেও:

struct Foo2 {
    int bar;
    ~Foo2() {  }
};

Foo2 test(Foo2 byval) { return byval; }

তুমি পাও:

test(Foo2):
        mov     edx, DWORD PTR [rsi]
        mov     rax, rdi
        mov     DWORD PTR [rdi], edx
        ret

অকেজো লোডিং এবং স্টোরেজ সহ।


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

1
দুর্ভাগ্যক্রমে, এটি অন্য উপায় (আমি সম্মত হলাম যে এর মধ্যে কিছু ইতিমধ্যে যুক্তির বাইরে) সুনির্দিষ্টভাবে বলতে গেলে: আমি নিশ্চিত নই যে আপনি যে কারণগুলি সরবরাহ করেছেন সেগুলি অবিশ্যি এমন কোনও এবিআইকে সরবরাহ করবে যা std::unique_ptrকোনও রেজিস্টারে অবিবাহিতভাবে বর্তমান পাস করার অনুমতি দেয় ।
কমিকসানসএমএস

3
"তুচ্ছ ডেস্ট্রাক্টর [উদ্ধৃতি প্রয়োজন]" স্পষ্টতই মিথ্যা; যদি কোনও কোড আসলে ঠিকানার উপর নির্ভর করে না, তবে যেমন-এর অর্থ ঠিকানাটি আসল মেশিনে থাকা প্রয়োজন না । অ্যাবস্ট্রাক্ট মেশিনে ঠিকানাটি অবশ্যই উপস্থিত থাকতে পারে তবে অ্যাবস্ট্রাক্ট মেশিনের যে জিনিসগুলির প্রকৃত মেশিনে কোনও প্রভাব পড়ে না সেগুলি হ'ল এমন জিনিসগুলি হ'ল যেন তা দূর করার অনুমতি দেওয়া হয়।
ইয়াক্ক - অ্যাডাম নেভ্রামামন্ট

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

1
@ আইনপোকলুম না, সেই বিমূর্ত মেশিনে সমস্ত কিছুর ঠিকানা রয়েছে; তবে ঠিকানাগুলি নির্দিষ্ট পরিস্থিতিতে কেবল পর্যবেক্ষণযোগ্য। registerশব্দ যে কার্যত এটিকে কঠিন শারীরিক মেশিনে "কোন ঠিকানা আছে" অবরুদ্ধ করে এটি একটি রেজিস্টার সংরক্ষণ কিছু শারীরিক মেশিনের জন্য তুচ্ছ করতে জন্যই ছিল।
ইয়াক্ক - অ্যাডাম নেভ্রামামন্ট

2

এটি কি আসলে কিছু প্ল্যাটফর্মে এবিআইয়ের প্রয়োজনীয়তা? (যা?) অথবা সম্ভবত এটি কিছু পরিস্থিতিতে কিছুটা হতাশার?

যদি কোনও প্রশংসনীয় ইউনিট সীমানায় দৃশ্যমান হয় তবে তা স্পষ্টভাবে বা স্পষ্টভাবে সংজ্ঞায়িত করা হোক না কেন এটি এবিআইয়ের অংশ হয়ে যায়।

এবিআই কেন এমন হয়?

মৌলিক সমস্যাটি হ'ল আপনি কল স্ট্যাকের উপরে ওঠার সাথে সাথে নিবন্ধগুলি সমস্ত সময় সংরক্ষণ এবং পুনরুদ্ধার হয়। সুতরাং তাদের কাছে একটি রেফারেন্স বা পয়েন্টার থাকা ব্যবহারিক নয়।

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

একটি তুচ্ছভাবে অনুলিপিযোগ্য টাইপগুলি নিবন্ধগুলিতে পাস করা যায় কারণ লজিকাল অনুলিপি অপারেশনটিকে দুটি ভাগে ভাগ করা যায়। কলকারী দ্বারা পরামিতিগুলি পাস করার জন্য ব্যবহৃত রেজিস্টারে প্যারামিটারগুলি অনুলিপি করা হয় এবং তারপরে কলি দ্বারা স্থানীয় ভেরিয়েবলে অনুলিপি করা হয়। স্থানীয় ভেরিয়েবলের মেমরির অবস্থান রয়েছে কিনা তা কেবল কলির উদ্বেগ নয়।

অন্যদিকে একটি অনুলিপি বা মুভ কনস্ট্রাক্টর অবশ্যই ব্যবহার করতে হবে যাতে তার অনুলিপি অপারেশনটি এভাবে বিভক্ত হয় না, সুতরাং এটি অবশ্যই মেমোরিতে পাস করা উচিত।

সি ++ স্ট্যান্ডার্ড কমিটি সাম্প্রতিক বছরগুলিতে, বা কখনও এই বিষয়টি নিয়ে আলোচনা করেছে?

মানদণ্ড সংস্থাগুলি এটি বিবেচনা করে থাকলে আমার কোনও ধারণা নেই।

আমার স্পষ্ট সমাধান হ'ল ল্যাঙ্গুজে সঠিক ধ্বংসাত্মক পদক্ষেপগুলি (একটি "বৈধ তবে অন্যথায় অনির্ধারিত রাষ্ট্রের বর্তমান অর্ধ-পথের বাড়ির চেয়ে)" ছড়িয়ে দেওয়া, তারপরে "তুচ্ছ ধ্বংসাত্মক পদক্ষেপের অনুমতি দেওয়ার জন্য কোনও ধরণের পতাকা চিহ্নিত করার একটি উপায় প্রবর্তন করা "এমনকি যদি এটি তুচ্ছ অনুলিপিগুলির অনুমতি দেয় না।

তবে এই জাতীয় সমাধানের জন্য বিদ্যমান ধরণের ক্ষেত্রে প্রয়োগের জন্য বিদ্যমান কোডের এবিআইকে ভেঙে ফেলতে হবে, যা প্রতিরোধের ন্যায্য বিট আনতে পারে (যদিও নতুন সি ++ স্ট্যান্ডার্ড সংস্করণগুলির ফলস্বরূপ এবিআই বিরতি নজিরবিহীন নয়, উদাহরণস্বরূপ স্টাডি :: স্ট্রিং পরিবর্তনগুলি সি ++ 11 এ এবিআই বিরতিতে ফলস্বরূপ ..


আপনি কীভাবে যথাযথ ধ্বংসাত্মক পদক্ষেপগুলি কোনও অনন্য_পট্রিকে একটি রেজিস্টারে পাস করার অনুমতি দেবেন তা ব্যাখ্যা করতে পারেন? এটি কি কারণ এটি ঠিকানার যোগ্য স্টোরেজটির প্রয়োজনীয়তা বাদ দিতে পারে?
einpoklum

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

যদিও আপনি একটি নিয়ম যোগ করতে চাইবেন যে কোনও সংকলক প্যারামিটার পাসটি নিয়মিত পদক্ষেপ হিসাবে প্রয়োগ করতে পারে বা একটি "তুচ্ছ ধ্বংসাত্মক পদক্ষেপ" এর পরে অনুলিপিটি নিশ্চিত করে যাতে প্যারামিটারটি আসে না কেন রেজিস্টারে পাস করা সর্বদা সম্ভব ছিল ensure
প্লাগওয়াশ

কারণ নিবন্ধের আকারটি একটি পয়েন্টার ধরে রাখতে পারে, তবে একটি অনন্য_পিটার কাঠামো? আকার (অনন্য_পাত্র <টি>) কী?
মেল ভিজো মার্টিনেজ

@ মেলভিসো মার্টিনেজ আপনি বিভ্রান্তিকর unique_ptrএবং shared_ptrশব্দার্থবিজ্ঞান হতে পারেন : shared_ptr<T>আপনাকে সিটির সরবরাহ করতে পারবেন 1) স্ট্র্যাটিক টাইপ ইউ delete x;ডাব্লু / এক্সপ্রেশন দিয়ে মুছে ফেলার জন্য প্রাপ্ত এক্সট্রাক্ট ইউ থেকে প্রাপ্ত একটি পিটিআর এক্স (যাতে আপনার এখানে ভার্চুয়াল ড্টরের প্রয়োজন নেই) 2) বা এমনকি একটি কাস্টম ক্লিনআপ ফাংশন। তার মানে রানটাটাইম shared_ptrস্টেটটি সেই তথ্য এনকোড করার জন্য নিয়ন্ত্রণ ব্লকের অভ্যন্তরে ব্যবহৃত হয় । OTOH এর unique_ptrমতো কোনও কার্যকারিতা নেই এবং এটি মুছে ফেলার আচরণকে এনকোড করে না রাজ্যে; ক্লিনআপ কাস্টমাইজ করার একমাত্র উপায় হ'ল অন্য টেম্পলেট ইনস্ট্যান্সেশন (অন্য শ্রেণির ধরণ) তৈরি করা।
কৌতূহলী

-1

প্রথমে আমাদের মান এবং রেফারেন্স দিয়ে পাস করার অর্থ কী ফিরে যেতে হবে।

জাভা এবং এসএমএলের মতো ভাষার ক্ষেত্রে, মান অনুসারে পাস করা সোজা হয় (এবং রেফারেন্সের মাধ্যমে কোনও পাস নেই) যেমন একটি ভেরিয়েবলের মান অনুলিপি করা হয়, যেমন সমস্ত ভেরিয়েবল কেবল স্কেলার এবং বিল্টিন কপি সিমেটিক: তারা হয় যাকে গণিত হিসাবে গণনা করা হয় সি ++, বা "রেফারেন্স" টাইপ করুন (বিভিন্ন নাম এবং সিনট্যাক্স সহ পয়েন্টার)।

সিতে আমাদের স্কেলার এবং ব্যবহারকারীর সংজ্ঞায়িত প্রকার রয়েছে:

  • স্কেলারের একটি সংখ্যাসূচক বা বিমূর্ত মান রয়েছে (পয়েন্টার সংখ্যা নয়, তাদের একটি বিমূর্ত মান রয়েছে) যা অনুলিপি করা হয়েছে।
  • সমষ্টিগত প্রকারগুলিতে তাদের সম্ভবত সমস্ত প্রাথমিক সদস্যদের অনুলিপি করা হয়েছে:
    • পণ্যের ধরণের জন্য (অ্যারে এবং কাঠামো): পুনরাবৃত্তভাবে, অ্যারের উপাদানগুলির কাঠামোর সমস্ত সদস্য এবং অনুলিপি করা হয় (সি ফাংশন সিনট্যাক্সগুলি সরাসরি মান অনুসারে অ্যারেগুলি পাস করা সম্ভব করে না, কেবল কোনও কাঠামোর সদস্যদের অ্যারে করে দেয়, তবে এটি বিশদ )।
    • যোগফলের জন্য (ইউনিয়ন): "সক্রিয় সদস্য" এর মান সংরক্ষণ করা হয়; স্পষ্টতই, সদস্যের অনুলিপি সদস্য ক্রমযুক্ত নয় কারণ সমস্ত সদস্যকে আরম্ভ করা যায় না।

সি ++ ব্যবহারকারীর সংজ্ঞায়িত প্রকারগুলিতে ব্যবহারকারী সংজ্ঞায়িত অনুলিপি অনুলিপি থাকতে পারে, যা তাদের সংস্থানগুলির মালিকানা এবং "গভীর অনুলিপি" ক্রিয়াকলাপের সাথে সত্যিকারের "অবজেক্ট ওরিয়েন্টেড" প্রোগ্রামিং সক্ষম করে। এই জাতীয় ক্ষেত্রে, অনুলিপি অপারেশন হ'ল একটি ফাংশনের কল যা প্রায় স্বেচ্ছাসেবী অপারেশন করতে পারে।

সি স্ট্রাক্টের জন্য সি ++ হিসাবে সংকলিত, "অনুলিপি" এখনও ব্যবহারকারী সংজ্ঞায়িত অনুলিপি অপারেশন (কনস্ট্রাক্টর বা অ্যাসাইনমেন্ট অপারেটর) হিসাবে সংজ্ঞায়িত, যা সংকলক দ্বারা সুস্পষ্টভাবে উত্পন্ন হয়। এর অর্থ হ'ল একটি সি / সি ++ সাধারণ উপসেট প্রোগ্রামের অর্থশাস্ত্রটি সি এবং সি ++ এর মধ্যে পৃথক: সিতে পুরো সামগ্রিক প্রকারটি অনুলিপি করা হয়, সি ++ এ প্রতিটি সদস্যের অনুলিপি করার জন্য একটি স্পষ্টতভাবে উত্পন্ন অনুলিপি ফাংশন বলা হয়; শেষ ফলাফল হ'ল উভয় ক্ষেত্রেই প্রতিটি সদস্য অনুলিপি করা হয়।

(আমি মনে করি, যখন কোনও ইউনিয়নের অভ্যন্তরীণ কাঠামো অনুলিপি করা হয় তখন ব্যতিক্রম হয় exception)

সুতরাং শ্রেণীর ধরণের জন্য, নতুন উদাহরণ তৈরির একমাত্র উপায় (ইউনিয়ন অনুলিপিগুলির বাইরে) একটি কনস্ট্রাক্টর (এমনকি তুচ্ছ সংকলক উত্পাদক কনস্ট্রাক্টরদের ক্ষেত্রেও) via

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

স্কেলার প্রকারের জন্য, কোনও বস্তুর মান হ'ল অবজেক্টের মূল্য, অবজেক্টে সঞ্চিত বিশুদ্ধ গাণিতিক মান।

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

সুতরাং মান দ্বারা পাস সত্যিই একটি জিনিস নয়: এটি কপি কনস্ট্রাক্টর কল দ্বারা পাস , যা কম সুন্দর। অনুলিপি নির্মাণকারীটি তার অভ্যন্তরীণ আক্রমণকারীদের (যা অভ্যন্তরীণ সি ++ বৈশিষ্ট্য নয়, বিমূর্ত ব্যবহারকারীর বৈশিষ্ট্যগুলি সম্মান করে) অবজেক্টের ধরণের যথাযথ শব্দার্থক অনুযায়ী একটি বুদ্ধিমান "কপি" অপারেশন করবেন বলে আশা করা হচ্ছে।

শ্রেণীর অবজেক্টের মান দ্বারা পাস করার অর্থ:

  • অন্য উদাহরণ তৈরি করুন
  • তারপরে সেই উদাহরণটিতে ফাংশন অ্যাক্টটি তৈরি করুন।

নোট করুন যে অনুলিপিটি নিজেই কোনও ঠিকানা সহ কোনও বস্তু কিনা: ইস্যুটির কোনও যোগসূত্র নেই: সমস্ত ফাংশন প্যারামিটারগুলি বস্তু এবং একটি ঠিকানা রয়েছে (ভাষা শব্দার্থক স্তরে)।

বিষয়টি হ'ল:

  • অনুলিপিটি একটি নতুন অবজেক্ট যা মাপসই হিসাবে মুল অবজেক্টের খাঁটি গাণিতিক মান (সত্য খাঁটি মূল্য) দিয়ে শুরু করা হয়;
  • বা অনুলিপি ক্লাসগুলির মতো মূল বস্তুর মান

তুচ্ছ শ্রেণীর ধরণের ক্ষেত্রে, আপনি এখনও মূলটির সদস্য কপির সদস্যকে সংজ্ঞায়িত করতে পারেন, তাই অনুলিপি অপারেশনগুলির অনুলিপি (অনুলিপি নির্মাণকারী এবং কার্যনির্বাহী) কারণে আপনি মূলটির খাঁটি মূল্য নির্ধারণ করতে পারেন। স্বেচ্ছাচারী বিশেষ ব্যবহারকারীর ফাংশনগুলির সাথে এটি নয়: আসলটির একটি মান একটি নির্ধারিত অনুলিপি হতে হবে।

শ্রেণীর অবজেক্টগুলি অবশ্যই কলার দ্বারা নির্মিত হতে হবে; কনস্ট্রাক্টরের আনুষ্ঠানিকভাবে একটি thisপয়েন্টার থাকে তবে আনুষ্ঠানিকতা এখানে প্রাসঙ্গিক নয়: সমস্ত অবজেক্টের আনুষ্ঠানিকভাবে একটি ঠিকানা থাকে তবে কেবল তাদের ঠিকানাটি *&i = 1;নিখরচায় স্থানীয়ভাবে ব্যবহার করা হয় (বিপরীতভাবে ঠিকানার স্থানীয় ব্যবহারের তুলনায়) একটি ভাল সংজ্ঞা দেওয়া দরকার ঠিকানা।

এই দুটি পৃথকভাবে সংকলিত ফাংশনগুলির একটি ঠিকানা থাকতে হবে যদি কোনও অবজেক্ট অবশ্যই ঠিকঠাক দ্বারা পাস করে:

void callee(int &i) {
  something(&i);
}

void caller() {
  int i;
  callee(i);
  something(&i);
}

এখানে এমনকি যদি something(address)একটি বিশুদ্ধ ফাংশন বা ম্যাক্রো বা যাই হোক না কেন (ভালো হয় printf("%p",arg)) যে ঠিকানা সংরক্ষণ করতে পারবেন না বা অন্য সত্তা যোগাযোগ, আমরা প্রয়োজন ঠিকানা দ্বারা পাস কারণ ঠিকানা ভাল একটি অনন্য বস্তুর জন্য নির্ধারণ করা আবশ্যক আছে intএকটি অনন্য আছে পরিচয়।

আমরা জানি না কোনও বহিরাগত ফাংশন এতে প্রবেশের ঠিকানাগুলির ক্ষেত্রে "খাঁটি" হবে কিনা।

এখানে সম্ভাব্য পারেন একটি অ তুচ্ছ কন্সট্রাকটর বা বিনাশকারী মধ্যে ঠিকানার একটি বাস্তব ব্যবহারের জন্য আহ্বানকারী পাশ সম্ভবত নিরাপদ, সরল রুট গ্রহণ এবং বস্তুর আহ্বানকারী তার ঠিকানা একটি পরিচয় দিতে এবং পাস, যেমন জন্য কারণ এটা তোলে নিশ্চিত হয়ে নিন যে কনস্ট্রাক্টর, নির্মাণের পরে এবং ডিস্ট্রাক্টরে তার ঠিকানার কোনও অ-তুচ্ছ ব্যবহার সামঞ্জস্যপূর্ণ : thisঅবশ্যই অবজেক্টের অস্তিত্বের উপরে একই হতে হবে।

অন্য কোনও ফাংশনের মতো একটি নন ট্রিভিয়াল কনস্ট্রাক্টর বা ডেস্ট্রাক্টর thisপয়েন্টারটি এমনভাবে ব্যবহার করতে পারে যার জন্য তার মানের সাথে সামঞ্জস্যতা প্রয়োজন যদিও এমনকী তুচ্ছ জিনিস সহ কিছু বস্তু নাও পারে:

struct file_handler { // don't use that class!
    file_handler () { this->fileno = -1; }
    file_handler (int f) { this->fileno = f; }
    file_handler (const file_handler& rhs) {
        if (this->fileno != -1)
            this->fileno = dup(rhs.fileno);
        else
            this->fileno = -1;
    }
    ~file_handler () {
        if (this->fileno != -1)
            close(this->fileno); 
    }
    file_handler &operator= (const file_handler& rhs);
};

মনে রাখবেন যে this->সেক্ষেত্রে, কোনও পয়েন্টার (স্পষ্ট স্পষ্ট বাক্য গঠন ) এর সুস্পষ্ট ব্যবহার সত্ত্বেও , অবজেক্টের পরিচয় অপ্রাসঙ্গিক: সংকলকটি সরানো এবং "কপিরাইট এলিজেন" করার জন্য বিটওয়াইসটি প্রায় ভালভাবে কপিরাইটটি ব্যবহার করতে পারে। এটি thisবিশেষ সদস্য ফাংশনগুলিতে ব্যবহারের "বিশুদ্ধতা" স্তরের উপর ভিত্তি করে (ঠিকানাটি এড়ায় না)।

তবে বিশুদ্ধতা স্ট্যান্ডার্ড ডিক্লেয়ারেশন লেভেলে কোনও বৈশিষ্ট্য উপলভ্য নয় (সংকলক এক্সটেনশানগুলি উপস্থিত রয়েছে যা নন-ইনলাইন ফাংশন ঘোষণায় বিশুদ্ধতার বিবরণ যুক্ত করে), সুতরাং আপনি কোডের বিশুদ্ধতার ভিত্তিতে কোনও এবিআইকে সংজ্ঞায়িত করতে পারবেন না যা উপলভ্য নয় (কোড হতে পারে এবং ইনলাইন এবং বিশ্লেষণের জন্য উপলব্ধ নাও থাকতে পারে)।

বিশুদ্ধতা "অবশ্যই বিশুদ্ধ" বা "অপরিষ্কার বা অজানা" হিসাবে পরিমাপ করা হয়। সাধারণ স্থল, বা শব্দার্থের উপরের সীমানা (প্রকৃতপক্ষে সর্বাধিক), বা এলসিএম (সর্বনিম্ন সাধারণ একাধিক) "অজানা"। সুতরাং এবিআই অজানা উপর স্থির।

সারসংক্ষেপ:

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

সম্ভাব্য ভবিষ্যতের কাজ:

খাঁটি বিশিষ্টতা কি সাধারণীকরণ এবং মানক করা যথেষ্ট?


1
আপনার প্রথম উদাহরণটি বিভ্রান্তিকর বলে মনে হচ্ছে। আমি মনে করি আপনি সাধারণভাবে একটি পয়েন্ট করছেন তবে প্রথমে আমি ভেবেছিলাম আপনি প্রশ্নের কোডটির সাথে সাদৃশ্য তৈরি করছেন। কিন্তু মানvoid foo(unique_ptr<int> ptr) অনুসারে শ্রেণিক বস্তু গ্রহণ করে । এই অবজেক্টটির একটি পয়েন্টার সদস্য রয়েছে, তবে আমরা শ্রেণি অবজেক্টটি নিজেই রেফারেন্স দ্বারা পাস হওয়ার কথা বলছি। (কারণ এটি তুচ্ছ-অনুলিপিযোগ্য নয় তাই এর নির্মাতা / ডেস্ট্রাক্টরের একটি সামঞ্জস্যতার প্রয়োজন )) এটি আসল যুক্তি এবং স্পষ্টভাবে রেফারেন্স দিয়ে পাস করার প্রথম উদাহরণের সাথে সংযুক্ত নয় ; সেক্ষেত্রে পয়েন্টারটি একটি রেজিস্টারে পাস করা হয়। this
পিটার

@ পিটারকর্ডস " আপনি প্রশ্নের কোডটির সাথে সাদৃশ্য তৈরি করছিলেন " আমি ঠিক তা-ই করেছি। " মান দ্বারা শ্রেণিক বস্তু " হ্যাঁ আমার সম্ভবত ব্যাখ্যা করা উচিত যে সাধারণভাবে কোনও শ্রেণীর অবজেক্টের "মান" বলে কোনও জিনিস নেই সুতরাং অ গণিতের জন্য মান দ্বারা "মান দ্বারা" হয় না। " সেই অবজেক্টটির পয়েন্টার সদস্য রয়েছে " "স্মার্ট পিটিআর" এর পিটিআর-জাতীয় প্রকৃতি অপ্রাসঙ্গিক; এবং তাই "স্মার্ট পিটিআর" এর পিটিআর সদস্য। একটি intপিটিআরটি কেবল একটি স্কেলারের মতো : আমি একটি "স্মার্ট ফাইলনো" উদাহরণ লিখেছিলাম যা চিত্রিত করে যে "পিআরটি বহন" এর সাথে "মালিকানা" এর কোনও যোগসূত্র নেই।
কৌতূহলী

1
শ্রেণীর বস্তুর মান হ'ল এর অবজেক্ট-প্রতিনিধিত্ব। জন্য unique_ptr<T*>, এটি একই আকার এবং বিন্যাস হিসাবে T*একটি রেজিস্টার ফিট করে in তুচ্ছ-অনুলিপিযোগ্য ক্লাস অবজেক্টগুলি বেশিরভাগ কলিং কনভেনশনগুলির মতো, x86-64 সিস্টেম ভি-তে রেজিস্টারগুলিতে মান দ্বারা পাস করা যেতে পারে । এই তোলে কপি এর unique_ptrবস্তু, আপনার অসদৃশ intউদাহরণ যেখানে callee এর &i হয় আহ্বানকারী এর ঠিকানা iকারণ আপনার রেফারেন্স দ্বারা পাস সি ++ পর্যায়ে শুধু একটি এস এম বাস্তবায়ন বিস্তারিত হিসাবে।
পিটার

1
এর, আমার শেষ মন্তব্যে সংশোধন। এটি শুধু একটি উপার্জন না কপি এর unique_ptrঅবজেক্ট; এটি ব্যবহার করছে std::moveসুতরাং এটি অনুলিপি করা নিরাপদ কারণ এর ফলস্বরূপ 2 টি অনুলিপি হবে না unique_ptr। তবে একটি তুচ্ছ-অনুলিপিযোগ্য টাইপের জন্য, হ্যাঁ, এটি পুরো সামগ্রিক সামগ্রীর অনুলিপি করে। যদি এটি কোনও একক সদস্য হয় তবে ভাল কলিং কনভেনশনগুলি এ জাতীয় ধরণের স্কেলারের মতোই আচরণ করে।
পিটার

1
ভালো দেখায়. নোটস: সি স্ট্রাক্টগুলি সি ++ হিসাবে সংকলিত - সি ++ এর মধ্যে পার্থক্য প্রবর্তনের জন্য এটি কোনও কার্যকর উপায় নয়। সি ++ struct{}এ সি ++ স্ট্রাক্ট। সম্ভবত আপনার "সাদামাটা স্ট্রাক্ট", বা "সি এর বিপরীতে" বলা উচিত। হ্যাঁ, কারণ একটি পার্থক্য আছে। আপনি যদি atomic_intস্ট্রাক্ট সদস্য হিসাবে ব্যবহার করেন, সি এটি অ-পরমাণুভাবে অনুলিপি করবে, মুছে ফেলা অনুলিপি নির্মাণকারীটিতে সি ++ ত্রুটি। আমি ভুলে যাই volatileসদস্যদের সাথে স্ট্রাইকগুলিতে সি ++ কী করে । সি আপনাকে struct tmp = volatile_struct;পুরো জিনিসটি অনুলিপি করতে দেবে (সিকলকের জন্য দরকারী); সি ++ করবে না।
পিটার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.