এসটিডি :: অনন্য_পিটার <টি> টি এর সম্পূর্ণ সংজ্ঞা জানতে প্রয়োজনীয়?


248

আমার একটি শিরোনামে এমন কিছু কোড রয়েছে যা দেখতে এই জাতীয় দেখাচ্ছে:

#include <memory>

class Thing;

class MyClass
{
    std::unique_ptr< Thing > my_thing;
};

আমি যদি এই Thingশিরোনামটিকে এমন কোনও সিপিতে অন্তর্ভুক্ত করি যা টাইপ সংজ্ঞাটি অন্তর্ভুক্ত করে না , তবে এটি ভিএস 2010-এসপি 1 এর অধীন সংকলন করে না:

1> সি: \ প্রোগ্রাম ফাইল (x86) \ মাইক্রোসফ্ট ভিজ্যুয়াল স্টুডিও 10.0 \ ভিসি \ অন্তর্ভুক্ত \ মেমরি (2067): ত্রুটি C2027: অপরিজ্ঞাত টাইপ 'থিং' ব্যবহার

std::unique_ptrদ্বারা প্রতিস্থাপন std::shared_ptrএবং এটি সংকলন।

সুতরাং, আমি অনুমান করছি যে এটি বর্তমান VS2010 std::unique_ptrএর বাস্তবায়ন যার সম্পূর্ণ সংজ্ঞা প্রয়োজন এবং এটি সম্পূর্ণ বাস্তবায়ন-নির্ভর।

অথবা এটা? এর স্ট্যান্ডার্ড প্রয়োজনীয়তাগুলির মধ্যে কি এমন কিছু রয়েছে যা std::unique_ptrকেবলমাত্র একটি অগ্রিম ঘোষণার সাথে কাজ করা কার্যকর করা অসম্ভব করে দেয় ? এটি অদ্ভুত বোধ করে যেহেতু এটি কেবল একটি পয়েন্টার ধরে রাখা Thingউচিত, তাই না?


20
যখন আপনি এবং না শ্রেষ্ঠ ব্যাখ্যা সি ++ 0x স্মার্ট পয়েন্টার সঙ্গে একটি সম্পূর্ণ টাইপ প্রয়োজন হাওয়ার্ড Hinnant কারো নির্দেশ চলে না "অসম্পূর্ণ ধরনের এবং shared_ptr/ unique_ptr" শেষে টেবিল আপনার প্রশ্নের উত্তর দিতে হবে।
জেমস ম্যাকনেলিস

17
পয়েন্টার জেমস জন্য ধন্যবাদ। আমি ভুলে গিয়েছিলাম কোথায় আমি এই টেবিলটি রেখেছি! :-)
হাওয়ার্ড হিন্যান্ট


5
@ জেমস ম্যাকনেলিস হাওয়ার্ড হিন্যান্টের ওয়েবসাইটের লিঙ্কটি নিচে রয়েছে। এটির ওয়েব.আরচিভ.অর্গ সংস্করণ এখানে । যাই হোক না কেন, তিনি একই বিষয়বস্তু দিয়ে নিচে একেবারে উত্তর দিয়েছেন :-)
Ela782

স্কট মেয়ার্সের কার্যকর আধুনিক সি ++ এর আইটেম 22 এ আরও একটি ভাল ব্যাখ্যা দেওয়া হয়েছে।
ফ্রেড শোয়েন

উত্তর:


328

এখান থেকে গৃহীত ।

সি ++ স্ট্যান্ডার্ড লাইব্রেরির বেশিরভাগ টেম্পলেটগুলির জন্য তাদের সম্পূর্ণ ধরণের মাধ্যমে ইনস্ট্যান্ট করা দরকার। তবে shared_ptrএবং unique_ptrএটি আংশিক ব্যতিক্রম। কিছু, তবে তাদের সমস্ত সদস্যেরই অসম্পূর্ণ প্রকারের সাথে তাত্ক্ষণিকভাবে চলতে পারে না। এর জন্য অনুপ্রেরণা হ'ল স্মার্ট পয়েন্টার ব্যবহার করে পিম্পলের মতো আইডিয়মগুলিকে সমর্থন করা এবং অপরিজ্ঞাত আচরণের ঝুঁকি না নিয়ে।

অপরিবর্তিত আচরণটি ঘটতে পারে যখন আপনার কোনও অসম্পূর্ণ প্রকার থাকে এবং আপনি deleteএটিতে কল করেন:

class A;
A* a = ...;
delete a;

উপরেরটি আইনি কোড legal এটি সংকলন করা হবে। আপনার সংকলক উপরের মত উপরের কোডের জন্য একটি সতর্কতা প্রেরণ করতে পারে বা নাও করতে পারে। এটি কার্যকর করা হলে, খারাপ জিনিস সম্ভবত ঘটবে। আপনি যদি খুব ভাগ্যবান হন তবে আপনার প্রোগ্রামটি ক্রাশ হবে। তবে আরও সম্ভাব্য ফলাফল হ'ল আপনার প্রোগ্রামটি নিঃশব্দে স্মৃতি ফাঁস ~A()করবে না যা বলা হয় না।

auto_ptr<A>উপরের উদাহরণে ব্যবহার করে কোনও লাভ হয় না। আপনি এখনও একই অপরিবর্তিত আচরণ পাবেন যেন আপনি কোনও কাঁচা পয়েন্টার ব্যবহার করেছেন।

তবুও, নির্দিষ্ট জায়গায় অসম্পূর্ণ ক্লাস ব্যবহার করা খুব দরকারী! এই যেখানে shared_ptrএবং unique_ptrসাহায্য। এই স্মার্ট পয়েন্টারগুলির মধ্যে একটির ব্যবহার আপনাকে সম্পূর্ণ অসম্পূর্ণ প্রকারের বাদ দিয়ে একটি অসম্পূর্ণ প্রকারের সাথে দূরে সরে যাবে। এবং সর্বাধিক গুরুত্বপূর্ণ, যখন একটি সম্পূর্ণ টাইপ থাকা প্রয়োজন, আপনি সেই সময়ে অসম্পূর্ণ টাইপের সাথে স্মার্ট পয়েন্টারটি ব্যবহার করার চেষ্টা করলে আপনি একটি সংকলন-সময় ত্রুটি পান।

আর কোনও অপরিজ্ঞাত আচরণ নেই:

যদি আপনার কোডটি কম্পাইল করে, তবে আপনি যে কোনও জায়গাতেই একটি সম্পূর্ণ টাইপ ব্যবহার করেছেন।

class A
{
    class impl;
    std::unique_ptr<impl> ptr_;  // ok!

public:
    A();
    ~A();
    // ...
};

shared_ptrএবং unique_ptrবিভিন্ন জায়গায় সম্পূর্ণ ধরণের প্রয়োজন। কারণগুলি অস্পষ্ট, একটি গতিশীল মুছক বনাম একটি স্ট্যাটিক মুছক এর সাথে করণীয়। সুনির্দিষ্ট কারণগুলি গুরুত্বপূর্ণ নয়। প্রকৃতপক্ষে, বেশিরভাগ কোডে একটি সম্পূর্ণ প্রকারের প্রয়োজন কোথায় তা সঠিকভাবে জানা আপনার পক্ষে গুরুত্বপূর্ণ নয়। কেবল কোড, এবং আপনি যদি এটি ভুল পান তবে সংকলক আপনাকে বলবে।

তবে এটি আপনার পক্ষে সহায়ক হলে এখানে একটি টেবিল রয়েছে যা সম্পূর্ণরূপে প্রয়োজনীয়তার সাথে সম্মত shared_ptrএবং বেশ কয়েকটি সদস্যের নথি দেয় unique_ptr। সদস্যটির যদি একটি সম্পূর্ণ প্রকারের প্রয়োজন হয়, তবে প্রবেশের "সি" রয়েছে, অন্যথায় সারণি এন্ট্রি "আই" দিয়ে পূর্ণ হবে।

Complete type requirements for unique_ptr and shared_ptr

                            unique_ptr       shared_ptr
+------------------------+---------------+---------------+
|          P()           |      I        |      I        |
|  default constructor   |               |               |
+------------------------+---------------+---------------+
|      P(const P&)       |     N/A       |      I        |
|    copy constructor    |               |               |
+------------------------+---------------+---------------+
|         P(P&&)         |      I        |      I        |
|    move constructor    |               |               |
+------------------------+---------------+---------------+
|         ~P()           |      C        |      I        |
|       destructor       |               |               |
+------------------------+---------------+---------------+
|         P(A*)          |      I        |      C        |
+------------------------+---------------+---------------+
|  operator=(const P&)   |     N/A       |      I        |
|    copy assignment     |               |               |
+------------------------+---------------+---------------+
|    operator=(P&&)      |      C        |      I        |
|    move assignment     |               |               |
+------------------------+---------------+---------------+
|        reset()         |      C        |      I        |
+------------------------+---------------+---------------+
|       reset(A*)        |      C        |      C        |
+------------------------+---------------+---------------+

পয়েন্টার ধর্মান্তর প্রয়োজনীয় যে কোনো অপারেশন উভয়ের জন্য সম্পূর্ণ ধরনের প্রয়োজন unique_ptrএবং shared_ptr

unique_ptr<A>{A*}কন্সট্রাকটর একটি পার পেয়ে যাবে অসম্পূর্ণ Aশুধুমাত্র কম্পাইলার একটি কল সেট আপ করার প্রয়োজন হয় না যদি ~unique_ptr<A>()। উদাহরণস্বরূপ, আপনি যদি unique_ptrগাদা রাখেন, আপনি একটি অসম্পূর্ণ সঙ্গে পেতে পারেন A। এই বিষয়টিতে আরও বিশদ বিশদটি এখানে পাওয়া যাবে ব্যারি দ্য হ্যাচেটের উত্তরে ।


3
দুর্দান্ত উত্তর। আমি পারলে +5 করতাম। আমি নিশ্চিত যে আমি আমার পরবর্তী প্রজেক্টে এর সাথে আবার উল্লেখ করব, যেখানে আমি স্মার্ট পয়েন্টারগুলির পুরো ব্যবহার করার চেষ্টা করছি।
ম্যাথিয়াস

4
যদি কেউ এই টেবিলটির অর্থ কী তা ব্যাখ্যা করতে পারে তবে আমার ধারণা এটি আরও বেশি লোককে সহায়তা করবে
গীতা

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

7
@ মেহরদাদ: সি ++ 98 এর জন্য এই সিদ্ধান্ত নেওয়া হয়েছিল, যা আমার সময়ের আগে। তবে আমি বিশ্বাস করি যে সিদ্ধান্তটি বাস্তবায়নের বিষয়ে উদ্বেগ এবং স্পেসিফিকেশনের অসুবিধা থেকে এসেছে (যেমন ঠিক কোন ধারকটির অংশগুলি সম্পূর্ণ ধরণের প্রয়োজন হয় না বা প্রয়োজন হয় না)। আজও, সি ++ 98 এর পরে 15 বছরের অভিজ্ঞতার সাথে, এই অঞ্চলে ধারক স্পেসিফিকেশন উভয়কেই শিথিল করা, এবং আপনি গুরুত্বপূর্ণ বাস্তবায়ন কৌশল বা অপ্টিমাইজেশানকে অবৈধভাবে অবরুদ্ধ করবেন না তা নিশ্চিত করা একটি তুচ্ছ তুচ্ছ কাজ হবে। আমি মনে করি এটি করা যেতে পারে। আমি জানি এটি অনেক কাজ হবে। আমি চেষ্টা করছি এক ব্যক্তি সচেতন।
হাওয়ার্ড হিন্যান্ট

9
কারণ উপরের মন্তব্যগুলি থেকে এটি স্পষ্ট নয়, যে কারও জন্য এই সমস্যা রয়েছে কারণ তারা unique_ptrএকটি শ্রেণীর সদস্য ভেরিয়েবল হিসাবে সংজ্ঞায়িত করেছেন , কেবল ক্লাস ঘোষণায় (শিরোনামের ফাইলে) একজন ডেস্ট্রাক্টর (এবং নির্মাণকারী) স্পষ্টভাবে ঘোষণা করুন এবং তাদের সংজ্ঞায়িত করতে এগিয়ে যান শিরোনাম ফাইলটিতে (এবং উত্স ফাইলটিতে পয়েন্ট টু ক্লাসের সম্পূর্ণ ঘোষণার সাথে শিরোনামটি রাখুন) শিরোনাম ফাইলটিতে কন্ট্রাক্টর বা ডিস্ট্রাক্টর সংকলকটিকে স্বয়ংক্রিয়ভাবে অন্তর্ভুক্ত করতে বাধা দিতে (যা ত্রুটিটি ট্রিগার করে)। stackoverflow.com/a/13414884/368896 এছাড়াও আমাকে এটির স্মরণ করিয়ে দিতে সহায়তা করে।
ড্যান নিসেনবাউম

42

মাইক্র্লাসের জন্য ডিফল্ট ডেস্ট্রাক্টর তৈরি করতে সংকলকটির থিংয়ের সংজ্ঞা প্রয়োজন। যদি আপনি স্পষ্টভাবে ডেস্ট্রাক্টর ঘোষণা করেন এবং এর (খালি) প্রয়োগটি সিপিপি ফাইলে সরিয়ে নিয়ে যান, কোডটি সংকলন করা উচিত।


5
আমি মনে করি এটি একটি ডিফল্ট ফাংশনটি ব্যবহার করার উপযুক্ত সুযোগ। MyClass::~MyClass() = default;বাস্তবায়নের ফাইলটি অবিচ্ছিন্নভাবে পরে কারও দ্বারা রাস্তা থেকে সরিয়ে ফেলা সম্ভব বলে মনে করেন যে ইচ্ছাকৃতভাবে ফাঁকা ছেড়ে রাখার পরিবর্তে বিবাদী দেহটি মুছে ফেলা হয়েছে বলে ধরে নিয়েছে।
ডেনিস জিকফুজ

@ ডেনিস জিকিফুজ: দুর্ভাগ্যক্রমে ওপি ভিসি ++ ব্যবহার করছে, এবং ভিসি ++ এখনও defaultএড এবং deleteডি শ্রেণীর সদস্যদের সমর্থন করে না ।
iljarn

6
.Cpp ফাইলে কীভাবে দরজা সরানো যায় তার জন্য +1। এছাড়াও মনে MyClass::~MyClass() = defaultহয় এটিকে কলংয়ের প্রয়োগ ফাইলে স্থানান্তরিত করে না। (এখনও?)
ইওনিল

: আপনি কন্সট্রাকটর বাস্তবায়ন সিপিপি ফাইলে, বনাম 2017. দেখতে উদাহরণস্বরূপ এই উত্তর সরাতে কমপক্ষে প্রয়োজন stackoverflow.com/a/27624369/5124002
jciloa

15

এটি বাস্তবায়ন নির্ভর নয়। এটি কাজ করার কারণটি হ'ল shared_ptrরান-টাইমে কল করতে সঠিক ডেস্ট্রাক্টরকে নির্ধারণ করে - এটি স্বাক্ষর টাইপের অংশ নয়। তবে unique_ptrএর ডেস্ট্রাক্টর তার ধরণের একটি অংশ, এবং এটি অবশ্যই সংকলন সময়ে জানা উচিত।


8

দেখে মনে হচ্ছে বর্তমান উত্তরগুলি ঠিক ডিফল্ট করছে না কেন ডিফল্ট কনস্ট্রাক্টর (বা ডেস্ট্রাক্টর) সমস্যা তবে সিপিতে ঘোষিত খালি উত্তরগুলি তা নয়।

এখানে যা ঘটছে তা এখানে:

যদি বাইরের শ্রেণীর (যেমন মাই ক্লাস) এর কনস্ট্রাক্টর বা ডেস্ট্রাক্টর না থাকে তবে সংকলকটি পূর্বনির্ধারিত একটি উত্পন্ন করে। এটির সাথে সমস্যা হ'ল সংকলকটি মূলত .hpp ফাইলে ডিফল্ট খালি কনস্ট্রাক্টর / ডেস্ট্রাক্টর সন্নিবেশ করে। এর অর্থ হ'ল ডিফল্ট কনট্রাক্টর / ডেস্ট্রাক্টরের কোডটি আপনার গ্রন্থাগারের বাইনারিগুলির সাথে নয়, হোস্ট এক্সিকিউটেবলের বাইনারি সহ সংকলিত হয়। তবে এই সংজ্ঞাগুলি সত্যই আংশিক ক্লাস তৈরি করতে পারে না। সুতরাং লিঙ্কার যখন আপনার লাইব্রেরির বাইনারিগুলিতে যায় এবং কনস্ট্রাক্টর / ডেস্ট্রাক্টর পেতে চেষ্টা করে তখন এটি কোনওটি খুঁজে পায় না এবং আপনি ত্রুটি পান। যদি কনস্ট্রাক্টর / ডেস্ট্রাক্টর কোডটি আপনার .cpp থাকে তবে আপনার লাইব্রেরির বাইনারিটিতে লিঙ্ক দেওয়ার জন্য এটি উপলব্ধ।

এটি অনন্য_সিটিআর বা ভাগ করা_পিটার ব্যবহারের সাথে কিছুই করার নয় এবং অন্যান্য উত্তরগুলি অনন্য_সিপি বাস্তবায়নের জন্য পুরানো ভিসি ++ তে বিভ্রান্তিকর বাগটি সম্ভবত বলে মনে হচ্ছে (ভিসি ++ 2015 আমার মেশিনে সূক্ষ্মভাবে কাজ করে)।

গল্পটি এতটাই নৈতিক যে আপনার শিরোনামটি কোনও নির্মাতা / ধ্বংসকারী সংজ্ঞা থেকে মুক্ত থাকা দরকার। এটি কেবল তাদের ঘোষণা থাকতে পারে। উদাহরণস্বরূপ, ~MyClass()=default;এইচপিপিতে কাজ করবে না। আপনি যদি সংকলককে ডিফল্ট কনস্ট্রাক্টর বা ডেস্ট্রাক্টর sertোকানোর অনুমতি দেন তবে আপনি একটি লিঙ্কারের ত্রুটি পাবেন।

অন্য একটি পক্ষের নোট: সিপিপি ফাইলে কনস্ট্রাক্টর এবং ডেস্ট্রাক্টর থাকার পরেও আপনি যদি এই ত্রুটিটি পেতে থাকেন তবে সম্ভবত সম্ভবত কারণটি হ'ল আপনার গ্রন্থাগারটি সঠিকভাবে সংকলিত হচ্ছে না। উদাহরণস্বরূপ, আমি একবার কনসোল থেকে ভিসি ++ তে লাইব্রেরিতে প্রজেক্টের ধরণটি সহজেই পরিবর্তন করেছিলাম এবং আমি এই ত্রুটিটি পেয়েছি কারণ ভিসি ++ _ _B প্রিপ্রোসেসর চিহ্নটি যোগ করেন নি এবং ঠিক একই ত্রুটি বার্তা তৈরি করেছে।


ধন্যবাদ! এটি একটি অবিশ্বাস্যভাবে অস্পষ্ট সি ++ স্পর্শের খুব সংক্ষিপ্ত ব্যাখ্যা ছিল। আমাকে অনেক ঝামেলা বাঁচিয়েছে।
জেপিএনটাড্রাগন

4

কেবল সম্পূর্ণতার জন্য:

শিরোনাম: আহ

class B; // forward declaration

class A
{
    std::unique_ptr<B> ptr_;  // ok!  
public:
    A();
    ~A();
    // ...
};

উত্স এ সি পি পি:

class B {  ...  }; // class definition

A::A() { ... }
A::~A() { ... }

বি শ্রেণীর সংজ্ঞা অবশ্যই কনস্ট্রাক্টর, ডেস্ট্রাক্টর এবং যে কোনও বিষয় যা বিচ্ছিন্নভাবে বি মুছে ফেলতে পারে তা দেখতে হবে (যদিও নির্মাণকারীর উপরের তালিকায় উপস্থিত নেই, ভিএস ২০১7-তে এমনকি কনস্ট্রাক্টর বি এর সংজ্ঞা প্রয়োজন এবং বিবেচনা করার সময় এটি বোধগম্য হয়) যে কনস্ট্রাক্টারে ব্যতিক্রম ঘটলে অনন্য_পিটারটি আবার ধ্বংস হয়ে যায়))


1

টেমপ্লেট ইনস্ট্যান্টেশনের পর্যায়ে থিংয়ের সম্পূর্ণ সংজ্ঞা প্রয়োজন। পিম্পল আইডিয়োম সংকলন করার সঠিক কারণ এটি।

যদি এটা সম্ভব ছিল না, মানুষের মত প্রশ্ন জিজ্ঞাসা করবে না এই


-2

এর সহজ উত্তরটি কেবল পরিবর্তে শেয়ারড_পিটার ব্যবহার করুন।


-7

আমার জন্য,

QList<QSharedPointer<ControllerBase>> controllers;

কেবল শিরোনাম অন্তর্ভুক্ত করুন ...

#include <QSharedPointer>

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