পাবলিক ফ্রেন্ড অদলবদলের ফাংশন


169

অনুলিপি-অনুলিপি-আইডিয়ামটির সুন্দর উত্তরে একটি কোডের টুকরো রয়েছে যা আমাকে একটু সাহায্যের দরকার:

class dumb_array
{
public:
    // ...
    friend void swap(dumb_array& first, dumb_array& second) // nothrow
    {
        using std::swap; 
        swap(first.mSize, second.mSize); 
        swap(first.mArray, second.mArray);
    }
    // ...
};

এবং তিনি একটি নোট যোগ করুন

অন্যান্য দাবি রয়েছে যে আমাদের প্রকারের জন্য আমাদের স্টাডি :: অদলবদলকে বিশেষজ্ঞ করা উচিত, একটি শ্রেণির অদলবদল পাশাপাশি একটি ফ্রি-ফাংশন সোয়াপ সরবরাহ করা উচিত But , এবং আমাদের ফাংশন ADL এর মাধ্যমে পাওয়া যাবে। একটি ফাংশন করবে।

সঙ্গে friendআমি "অনাত্মীয়" শর্তে একটি বিট, আমি সত্য বলিয়া স্বীকার করা উচিত নয়। সুতরাং, আমার প্রধান প্রশ্নগুলি হ'ল:

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

সাইড-প্রশ্ন:

  • সি ++ 11, আমি আমার চিহ্নিত করা উচিত swapসঙ্গে গুলিnoexcept ?
  • সি ++ 11 এবং তার সঙ্গে পরিসীমা-জন্য , আমি স্থান উচিত friend iter begin()এবং friend iter end()শ্রেণীর ভিতরের একই ভাবে? আমার মনে হয় friendএখানে দরকার নেই, তাই না?

এর জন্য পরিসর-ভিত্তিক সম্পর্কে পার্শ্ব প্রশ্নটি বিবেচনা করে: সদস্য ফাংশনগুলি লিখতে এবং স্ট্যান্ড নেমস্পেসে (§24.6.5) অভ্যন্তরীণভাবে অভ্যন্তরীণভাবে ব্যবহারের জন্য পরিসর-ভিত্তিক পরিসীমা ভিত্তিক বা স্ট্যান্ডের নাম (স্পেস) শেষে () এবং শেষ () এ সীমা অ্যাক্সেস ছেড়ে দেওয়া ভাল ধারণা or স্ট্যান্ডার্ড নেমস্পেস (দেখুন §6.5.4)। তবে, এটি খারাপ দিক দিয়ে আসে যে এই ফাংশনগুলি <iterator> শিরোনামের অংশ, আপনি যদি এটি অন্তর্ভুক্ত না করেন তবে আপনি সেগুলি নিজেই লিখতে চাইতে পারেন want
ভিটাস

2
কেন এটি স্থির নয় - কারণ কোনও friendফাংশন মোটেই সদস্য ফাংশন নয়।
aschepler

উত্তর:


175

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


আমরা প্রথমে দেখতে পাচ্ছি যে ধারকগুলির মধ্যে std::vector<>একক-যুক্তি সদস্যের ফাংশন রয়েছে swap, যেমন:

struct vector
{
    void swap(vector&) { /* swap members */ }
};

স্বাভাবিকভাবেই, তাহলে, আমাদের ক্লাসেরও উচিত, তাই না? সত্যিই ভাল না. স্ট্যান্ডার্ড লাইব্রেরিতে সব ধরণের অপ্রয়োজনীয় জিনিস রয়েছে এবং এর swapমধ্যে অন্যতম সদস্য । কেন? এর উপর যান.


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

ঠিক আছে, std::swapকাজ করার জন্য আমাদের std::vector<>একটি বিশেষত্ব প্রদান করা উচিত (এবং দেওয়া উচিত ছিল) std::swap, তাই না?

namespace std
{
    template <> // important! specialization in std is OK, overloading is UB
    void swap(myclass&, myclass&)
    {
        // swap
    }
}

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

namespace std
{
    template <typename T>
    void swap<T>(myclass<T>&, myclass<T>&) // error! no partial specialization
    {
        // swap
    }
}

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


এখানে! আমরা একটি friendফাংশন ব্যবহার করতে পারি এবং এটি ADL এর মাধ্যমে খুঁজে পেতে পারি :

namespace xyz
{
    struct myclass
    {
        friend void swap(myclass&, myclass&);
    };
}

আমরা swap 'র কিছু করতে চান, তখন আমরা শরীক std::swap এবং তারপর একটি অযোগ্য কল করতে:

using std::swap; // allow use of std::swap...
swap(x, y); // ...but select overloads, first

// that is, if swap(x, y) finds a better match, via ADL, it
// will use that instead; otherwise it falls back to std::swap

কি friend ফাংশন কি? এই অঞ্চলটিকে ঘিরে বিভ্রান্তি রয়েছে।

আগে সি ++ আদর্শায়িত করা হয়েছিল, friendফাংশন কিছু "বন্ধু নাম ইনজেকশন" বলা হয়, যেখানে কোড ভদ্র করেনি হিসাবে যদি যদি ফাংশন পার্শ্ববর্তী নামস্থান লেখা হয়েছে। উদাহরণস্বরূপ, এগুলি পূর্বমানের সমতুল্য ছিল:

struct foo
{
    friend void bar()
    {
        // baz
    }
};

// turned into, pre-standard:    

struct foo
{
    friend void bar();
};

void bar()
{
    // baz
}

যাইহোক, যখন ADL আবিষ্কার করা হয়েছিল তখন এটি সরানো হয়েছিল। friendফাংশন তারপর পারে শুধুমাত্র ADL মাধ্যমে পাওয়া যাবে; আপনি যদি এটি একটি ফ্রি ফাংশন হিসাবে চান, এটি এটি হিসাবে ঘোষণা করা দরকার (এটি দেখুন , উদাহরণস্বরূপ)। তবে ল! একটা সমস্যা ছিল.

আপনি যদি কেবল ব্যবহার করেন তবে std::swap(x, y)আপনার ওভারলোডটি কখনই পাওয়া যাবে না , কারণ আপনি স্পষ্টভাবে বলেছিলেন "দেখুন std, আর কোথাও নেই"! এ কারণেই কিছু লোক দুটি ফাংশন লেখার পরামর্শ দিয়েছেন: একটি ফাংশন হিসাবে ADL এর মাধ্যমে পাওয়া যায় এবং অন্যটি স্পষ্টত std::যোগ্যতাগুলি পরিচালনা করতে ।

তবে আমরা যেমন দেখেছি, এটি সব ক্ষেত্রেই কাজ করতে পারে না এবং আমরা কুৎসিত জগাখিচুড়ি দিয়ে শেষ করি। পরিবর্তে, অভিব্যক্তিগত অদলবদল অন্য পথে চলে গেছে: ক্লাসগুলির সরবরাহ করার জন্য এটি তৈরি করার পরিবর্তে, std::swapতারা যোগ্য ব্যবহার না করেছে তা নিশ্চিত করার জন্য এটি অদলবদলের কাজ it'sswap , ওপরের মতো । এবং এটি বেশ ভাল কাজ করার ঝোঁক দেয়, যতক্ষণ না লোকেরা এটি সম্পর্কে জানেন। তবে এর মধ্যে সমস্যাটি রয়েছে: অযোগ্য কলটি ব্যবহার করা দরকার u

এটি আরও সহজ করার জন্য, বুস্টের মতো কিছু লাইব্রেরি ফাংশন সরবরাহ করেছিল boost::swapযা কেবলমাত্র একটি অযোগ্য কল করে swap, এর সাথেstd::swap এই ক্ষেত্রে সংশ্লিষ্ট নামস্থান হিসাবে। এটি জিনিসগুলিকে আবার সংহত করতে সহায়তা করে, তবে এটি এখনও একটি ঝাঁকুনি।

মনে রাখবেন যে সি ++ 11 এর আচরণে কোনও পরিবর্তন নেই std::swap, যা আমি এবং অন্যরা ভুল করে ভেবেছিলাম কেস হবে। আপনি যদি এই দ্বারা বিট ছিল, এখানে পড়ুন


সংক্ষেপে: সদস্য ফাংশনটি কেবল গোলমাল, বিশেষীকরণটি কুরুচিপূর্ণ এবং অসম্পূর্ণ, তবে friendফাংশনটি সম্পূর্ণ এবং কাজ করে। আর তুমি অদলবদল যখন, হয় ব্যবহার boost::swapঅথবা একটি অযোগ্য swapসঙ্গে std::swapযুক্ত।


Forma অনানুষ্ঠানিকভাবে, কোনও নাম যুক্ত হয় যদি এটি কোনও ফাংশন কলের সময় বিবেচনা করা হয়। বিশদ জন্য, পড়ুন .43.4.2। এই ক্ষেত্রে, std::swapসাধারণত বিবেচনা করা হয় না; তবে আমরা এটি সংযুক্ত করতে পারি (এটি অযোগ্য হিসাবে বিবেচিত ওভারলোডের সেটে যোগ করুন swap), এটি সন্ধান করার অনুমতি দিয়ে।


10
আমি অসম্মতি জানাই যে সদস্যের কার্যটি কেবল গোলমাল। একটি সদস্য ফাংশন উদাহরণস্বরূপ অনুমতি দেয় std::vector<std::string>().swap(someVecWithData);, যা একটি swapফাংশন দিয়ে সম্ভব নয় কারণ উভয় যুক্তিই অবিচ্ছিন্ন রেফারেন্স দ্বারা পাস করা হয়।
iljarn

3
@ এল্ডজার্ন: আপনি এটি দুটি লাইনে করতে পারেন। সদস্য ফাংশন থাকা DRY নীতি লঙ্ঘন করে।
GManNickG

4
@ জিএমএন: ডিআরওয়াই নীতি প্রয়োগ হয় না যদি অন্যটির শর্তে একটি প্রয়োগ করা হয়। তা না হলে কোন এক বাস্তবায়নের সঙ্গে একটি বর্গ প্রচার হবে operator=, operator+এবং operator+=, কিন্তু পরিষ্কারভাবে প্রাসঙ্গিক শ্রেণীর সেই অপারেটার গৃহীত হয় / প্রতিসাম্য জন্য বিদ্যমান বলে আশা করা। আমার মতামত অনুসারে একই সদস্য swap/ নেমস্পেসের জন্য যায় swap
ইল্ডজার্ন

3
@ জিএমান আমি মনে করি এটি খুব বেশি ফাংশন বিবেচনা করছে। অল্প পরিচিত, তবে এমনকি একটি function<void(A*)> f; if(!f) { }ব্যর্থ হতে পারে কারণ এটি Aঘোষণা করে operator!যা fসমানভাবে fনিজেরও গ্রহণ করে operator!(অসম্ভব, তবে ঘটতে পারে)। তাহলে function<>, যে মারাত্মক হবে ' "অপারেটর bool, অপারেটর', কেন আমি বাস্তবায়ন করা উচিত! '? শুষ্ক লঙ্ঘন করবে! গুলি লেখক চিন্তা ওহ আমি একজন আছে'"। আপনার কেবলমাত্র একটি operator!বাস্তবায়ন করা দরকার Aএবং এর Aজন্য একজন কনস্ট্রাক্টর থাকা function<...>এবং জিনিসগুলি ভেঙে যায়, কারণ উভয় প্রার্থীরই ব্যবহারকারীর সংজ্ঞায়িত রূপান্তরগুলির প্রয়োজন হবে।
জোহানেস স্কাউব -

1
আসুন বিবেচনা করা যাক আমরা কীভাবে একটি [সদস্য] অদলবদল ফাংশন লেখার বিষয়ে ভাবতে পারি। স্বাভাবিকভাবেই, তাহলে, আমাদের ক্লাসেরও উচিত, তাই না? সত্যিই ভাল না. স্ট্যান্ডার্ড লাইব্রেরিতে সব ধরণের অপ্রয়োজনীয় জিনিস রয়েছে এবং এর মধ্যে একটি সদস্যের অদলবদল। লিঙ্কযুক্ত গটডাব্লু সদস্য অদলবদল ফাংশনের পক্ষে রয়েছে।
Xeverous

7

এই কোডটি সমান ( প্রায় প্রতিটি উপায়ে):

class dumb_array
{
public:
    // ...
    friend void swap(dumb_array& first, dumb_array& second);
    // ...
};

inline void swap(dumb_array& first, dumb_array& second) // nothrow
{
    using std::swap; 
    swap(first.mSize, second.mSize); 
    swap(first.mArray, second.mArray);
}

শ্রেণীর ভিতরে সংজ্ঞায়িত একটি বন্ধু ফাংশন:

  • বেষ্টনী নেমস্পেসে স্থাপন করা হয়েছে
  • স্বয়ংক্রিয়ভাবে inline
  • ক্লাসের স্থির সদস্যদের আরও যোগ্যতা ছাড়াই উল্লেখ করতে সক্ষম

সঠিক বিধিগুলি বিভাগে রয়েছে [class.friend](আমি সি ++ 0x খসড়ার 6 এবং 7 অনুচ্ছেদের উদ্ধৃতি দিয়েছি):

কোনও শ্রেণিটির বন্ধু ঘোষণায় একটি ফাংশন সংজ্ঞায়িত করা যায় যদি এবং কেবলমাত্র ক্লাসটি কোনও স্থানীয়-শ্রেণীর না হয় (9.8), ফাংশনটির নাম অযোগ্য হয় এবং ফাংশনটির নাম স্থান হয়।

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


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

1
আসলেই সমতুল্য নয়। প্রশ্নের কোডটি এটিকে এমন করে তোলে swapযা কেবলমাত্র ADL- এর কাছে দৃশ্যমান visible এটি বদ্ধ নেমস্পেসের সদস্য, তবে এটির নাম অন্য নাম সন্ধানের ফর্মগুলির কাছে দৃশ্যমান নয়। সম্পাদনা: আমি দেখতে পাচ্ছি যে @ জিএমএন আবারও দ্রুত ছিল :) :) আগেও আইএসও সি ++ তে সর্বদা সেভাবেই ছিল :)
জোহানেস স্কাউব - লিটব

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

2
@ টাওই: যেহেতু বন্ধুত্বের কার্যকারিতাটি নামকরণের সুযোগে রয়েছে, তাই আপনার তিনটি প্রশ্নের উত্তর পরিষ্কার হওয়া উচিত: (1) এটি একটি ফ্রি ফাংশন, এবং ক্লাসের ব্যক্তিগত এবং সুরক্ষিত সদস্যদের কাছে বন্ধু অ্যাক্সেস রয়েছে। (২) এটি মোটেই সদস্য নয়, উদাহরণ বা স্থিরও নয়। (3) এডিএল ক্লাসের মধ্যে অনুসন্ধান করে না তবে এটি ঠিক আছে কারণ বন্ধুর ফাংশনটির নেমস্পেসের সুযোগ রয়েছে।
বেন ভয়েগট

1
@Ben। বিশ্লেষণে, ফাংশনটি একটি নেমস্পেস সদস্য, এবং "ফাংশনটির নেমস্পেসের সুযোগ রয়েছে" এই বাক্যাংশটি ব্যাখ্যা করা যেতে পারে যে ফাংশনটি একটি নাম স্থানের সদস্য (এটি প্রায়শই এই জাতীয় বিবৃতি প্রসঙ্গে নির্ভর করে)। এবং এটি সেই নামস্থানে এমন একটি নাম যুক্ত করেছে যা কেবলমাত্র এডিএল-এর কাছে দৃশ্যমান (আসলে, আইআইআরসি কিছু অংশই কোনও নাম যুক্ত করা হয়েছে কিনা তা নিয়ে অনুমানের অন্যান্য অংশগুলির সাথে বিরোধিতা করে But তবে এতে যুক্ত হওয়া অসম্পূর্ণ ঘোষণাগুলি সনাক্ত করতে একটি নাম যুক্ত করা প্রয়োজন needed নেমস্পেস, সুতরাং বাস্তবে, একটি অদৃশ্য নাম যুক্ত করা হয়েছে 3..৩.১৪.৪৪ নোটটি দেখুন)।
জোহানেস স্কাউব - 19
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.