আমি কি সি ++ তে স্বায়ত্তশাসিত `স্ব-সদস্যের ধরণের প্রয়োগ করতে পারি?


101

সি ++ এর মধ্যে পিএইচপি-র কীওয়ার্ডের সমতুল্য অভাব রয়েছে , যা সংক্রামক শ্রেণীর ধরণের মূল্যায়ন করে।self

এটি প্রতি শ্রেণীর ভিত্তিতে জাল করা যথেষ্ট সহজ:

struct Foo
{
   typedef Foo self;
};

তবে আমাকে Fooআবার লিখতে হয়েছিল । হয়তো আমি একদিন এই ভুল পেয়েছি এবং একটি নীরব ত্রুটি সৃষ্টি করব।

decltypeএই কাজটিকে "স্বায়ত্তশাসিতভাবে" তৈরি করতে আমি কি কিছু বন্ধু এবং বন্ধুদের ব্যবহার করতে পারি ? আমি নিম্নলিখিতগুলি ইতিমধ্যে চেষ্টা করেছি তবে thisসে জায়গায় এটি বৈধ নয়:

struct Foo
{
   typedef decltype(*this) self;
};

// main.cpp:3:22: error: invalid use of 'this' at top level
//     typedef decltype(*this) self;

(আমি এর সমতুল্য সম্পর্কে চিন্তা করতে যাচ্ছি না static, যা একই কাজ করে তবে দেরীতে আবদ্ধ হয়))


9
this_tনিয়মিত সি ++ নামকরণের সাথে সম্ভবত আরও সংযুক্ত হবে।
বারটেক বানাচেউইচজ

3
@ বারটেকবাঞ্চেউইচজ: বা এই_প্রকার
প্লাজমা এইচএইচ

10
@ প্রিটোরিয়ান, আমি মনে করতে পারি না এটি প্রস্তাব ছিল কি না, তবে কেউ পরামর্শ দিয়েছেন auto()এবং ~auto()সিটার / ডিটারদের জন্য। কমপক্ষে বলার জন্য আকর্ষণীয়। যদি সেই উদ্দেশ্যে ব্যবহার করা হয়, সম্ভবত typedef auto self;, তবে এটি আমার কাছে কিছুটা স্কেচি মনে হয়।
ক্রিস

11
থাকলে আমি সত্যি সিনট্যাক্স এই সম্ভব করতে হবে সেটির পরামর্শ যাচ্ছিল, তাহলে সম্ভবত হবে decltype(class)হয়তো সঙ্গে decltype(struct)সমতুল্য। এটি কেবলমাত্র autoএকটি নির্দিষ্ট প্রসঙ্গে চেয়ে অনেক পরিষ্কার এবং এটির ভিত্তিতে ভাষার সাথে মানানসই কোনও সমস্যা আমি দেখতে পাচ্ছি না decltype(auto)
ক্রিস

11
যেহেতু আপনি ত্রুটিগুলি এড়াতে চান, আপনি স্ট্যাটিক_সেটর সহ একটি ডমি সদস্য ফাংশন সেট আপ করতে পারেন, যেমন void _check() { static_assert(std::is_same<self&, decltype(*this)>::value, "Correct your self type"); }শ্রেণি টেম্পলেটগুলির সাথে কাজ করে না, যদিও ...
সহস্রাব্দ

উত্তর:


38

ফু এর ধরণের পুনরাবৃত্তি না করে আপনি এটি কীভাবে করতে পারেন তা এখানে:

template <typename...Ts>
class Self;

template <typename X, typename...Ts>
class Self<X,Ts...> : public Ts...
{
protected:
    typedef X self;
};

#define WITH_SELF(X) X : public Self<X>
#define WITH_SELF_DERIVED(X,...) X : public Self<X,__VA_ARGS__>

class WITH_SELF(Foo)
{
    void test()
    {
        self foo;
    }
};

আপনি যদি সেখান থেকে পেতে চান Fooতবে আপনার WITH_SELF_DERIVEDনিম্নলিখিত পদ্ধতিতে ম্যাক্রো ব্যবহার করা উচিত :

class WITH_SELF_DERIVED(Bar,Foo)
{
    /* ... */
};

এমনকি আপনি যতগুলি বেস ক্লাসিকে চান তার সাথে একাধিক উত্তরাধিকারও করতে পারেন (ভ্যারিয়েডিক টেম্পলেট এবং ভ্যারায়ডিক ম্যাক্রোকে ধন্যবাদ):

class WITH_SELF(Foo2)
{
    /* ... */
};

class WITH_SELF_DERIVED(Bar2,Foo,Foo2)
{
    /* ... */
};

আমি এটি জিসিসি 4.8 এবং ঝাঁকুনি 3.4 এ কাজ করতে যাচাই করেছি।


18
আমি অনুমান করি উত্তরটি "না, তবে রাল্ফ পারে!" ;)
অরবিট

3
এটি কীভাবে কোনওভাবে টাইপডেফকে সেখানে রাখার চেয়ে উন্নত? এবং ,শ্বর, আপনি এমনকি টাইপডেফের প্রয়োজন হবে কেন? কেন?
মাইলস রাউট

7
@ মাইলসআরআউট এটি প্রশ্নের উত্তর, উত্তর নয়। সফ্টওয়্যার বিকাশের অনেক ক্ষেত্রে (এবং বিশেষত রক্ষণাবেক্ষণ) কোডে অপ্রয়োজনীয়তা এড়াতে সহায়ক, যাতে কোনও স্থানে কিছু পরিবর্তন করা আপনার অন্য জায়গায় কোড পরিবর্তন করার প্রয়োজন না হয়। যে সমগ্র বিন্দু autoএবং decltypeঅথবা এই ক্ষেত্রে self
রাল্ফ ট্যান্ডেটস্কি

1
template<typename T>class Self{protected: typedef T self;}; class WITH_SELF(Foo) : public Bar, private Baz {};সহজ হতে পারত এবং উত্তরাধিকারের উপর আরও সঠিক নিয়ন্ত্রণের অনুমতি দিত - এর বিরুদ্ধে কোনও কারণ?
অ্যাকোনকাগুয়া

@ মিমি মিমম্ম, যদি আপনি "নিজেকে পুনরাবৃত্তি করবেন না" নীতির গভীরভাবে প্রশংসা করতে না শিখেন তবে সম্ভবত আপনি এখনও যথেষ্ট / গুরুত্ব সহকারে কোডিং করেন নি। এই "বিশৃঙ্খলা" (এটি থেকে অনেক দূরে, আসলে) একটি অদ্বিতীয় ভাষা বৈশিষ্ট্য সম্পর্কে কথা বলার প্রসঙ্গে (বা কৃপণতা, এমনকি কিছু কঠোর পদক্ষেপের ঘাটতি) একেবারে মার্জিত সমাধান।
জেড

38

একটি সম্ভাব্য কাজ (আপনি এখনও টাইপ একবার লিখতে হবে):

template<typename T>
struct Self
{
protected:
    typedef T self;
};

struct Foo : public Self<Foo>
{
    void test()
    {
        self obj;
    }
};

আরও সুরক্ষিত সংস্করণের জন্য আমরা নিশ্চিত করতে পারি যে Tআসলে থেকে প্রাপ্ত Self<T>:

Self()
{
    static_assert(std::is_base_of<Self<T>, T>::value, "Wrong type passed to Self");
}

লক্ষ করুন যে কোনও static_assertসদস্য ফাংশনের অভ্যন্তরে সম্ভবত চেক করার একমাত্র উপায়, কারণগুলি std::is_base_ofসম্পূর্ণ হতে হবে passed


4
typenameটাইপিডেফের দরকার নেই । এবং যেহেতু এটি অপ্রয়োজনীয়দের সংখ্যা হ্রাস করে না আমি মনে করি এটি একটি কার্যকর বিকল্প নয়।
কনরাড রুডল্ফ

Fooনামটি পুনরাবৃত্তি করার ক্ষেত্রে এটি ঠিক একই সমস্যা ।
বারটেক বানাচেউইচজ

6
এটা তোলে হয় সীমিতভাবে মূল পদ্ধতির চেয়ে ভাল, যদিও, যেহেতু পুনরাবৃত্তি খুব ঘনিষ্ঠ একসঙ্গে হয়। প্রশ্নের সমাধান নয়, সেরা-কেস কাজের জন্য উপযুক্ত চেষ্টা করার জন্য +1।
অরবিটে

4
আমি কয়েকবার সমাধানটি ব্যবহার করেছি, এবং এটিতে একটি বিএডি জিনিস রয়েছে: পরে যখন উত্সাহিত হয় Foo, তখন আপনাকে হয়: (1) টি টি উপরের দিকে পাতা-বংশধর পর্যন্ত প্রচার করুন, বা (২) সেল্টটি থেকে বহুবার উত্তরাধিকার সূত্রে মনে রাখতে হবে , বা (3) স্বীকার করুন যে সমস্ত বাচ্চাকে বেস হিসাবে ব্যবহার করা যায় .. ব্যবহারযোগ্য, তবে ব্যাখ্যামূলক।
কোয়েটজলকোটল

@ কোয়েটজলকোয়াটল: যেহেতু আমি এর selfচেয়ে অনুলিপি করার চেষ্টা করছি তাই staticসমস্যা নেই।
স্বল্পতার রেস

33

আপনি নিয়মিত শ্রেণির ঘোষণার পরিবর্তে ম্যাক্রো ব্যবহার করতে পারেন, এটি আপনার পক্ষে তা করবে।

#define CLASS_WITH_SELF(X) class X { typedef X self;

এবং তারপর মত ব্যবহার করুন

CLASS_WITH_SELF(Foo) 
};

#define END_CLASS }; সম্ভবত পাঠযোগ্যতা সাহায্য করবে।


আপনি @ পরানাইক্সও নিতে Selfপারেন এবং এটি ব্যবহার করতে পারেন (এটি সত্যই হ্যাকিশ হতে শুরু করে)

#define WITH_SELF(X) X : public Self<X>

class WITH_SELF(Foo) {
};

18
EWWWWCCSS। এটি সম্পূর্ণ অপ্রয়োজনীয়।
কুকুরছানা

31
@ ডেড এমএমজি আমি মনে করি কিছু লোকেরা আরও বেশি ধারাবাহিকতা পছন্দ করতে পারে; সর্বোপরি, প্রথম ম্যাক্রোর ব্যবহারটি শেষ হয় না {, তাই }হ'ল "ঝুলন্ত", যা সম্ভবত পাঠ্য সম্পাদকরাও অপছন্দ করবেন।
বারটেক বানাচেউইচজ

6
দুর্দান্ত ধারণা তবে আমি ম্যাক্রোগুলির বিরুদ্ধে মূলত বিরোধী না হলেও আমি যদি কেবল সি ++ স্কোপিংয়ের অনুকরণ করি তবে এটি এখানে ব্যবহারযোগ্য হয় CLASS_WITH_SELF(foo) { … };- এবং আমি মনে করি এটি অর্জন করা অসম্ভব।
কনরাড রুডল্ফ

2
@ কনরাড রুডল্ফ আমিও এটি করার একটি উপায় যুক্ত করেছি। আমি এটি পছন্দ করি তা নয়, কেবলমাত্র সম্পূর্ণতার জন্য
বারটেক বানাচেউইচজ

1
যদিও এই পদ্ধতির সাথে কিছু সমস্যা রয়েছে। প্রথমটি তুমি ক্লাসে উত্তরাধিকারী সহজে করতে (যদি না আপনি অন্য ম্যাক্রো প্যারামিটার (গুলি) ব্যবহার করুন) অনুমতি দেয় না, এবং দ্বিতীয় সব সমস্যা রয়েছে থেকে উত্তরাধিকার সূত্রে প্রাপ্ত সেই Selfহয়েছে।
বারটেক বানাচেউইচজ

31

আমার কাছে কোনও ইতিবাচক প্রমাণ নেই তবে আমি মনে করি এটি অসম্ভব। আপনার প্রচেষ্টা হিসাবে একই কারণে নিম্নলিখিত ব্যর্থ হয়েছে - এবং আমি মনে করি এটিই আমরা পেতে পারি:

struct Foo {
    auto self_() -> decltype(*this) { return *this; }

    using self = decltype(self_());
};

মূলত, কি এই প্রমান করে যে সুযোগ যা আমরা আমাদের typedef ডিক্লেয়ার করতে চান কেবল আছে কোন অ্যাক্সেস করতে (এটা প্রত্যক্ষ বা পরোক্ষ হতে) this, এবং অন্য কোন (কম্পাইলার স্বাধীন) বর্গ 'টাইপ বা নামের পাবার উপায়।


4
এটি কি সম্ভবত সি ++ 1 ই রিটার্নের টাইপের ছাড়ের সাথে থাকবে?
ডায়াপ

4
@dyp আমার উত্তরের উদ্দেশ্যে যা কোনও পরিবর্তন করবে না। এখানে ত্রুটিটি পিছনের রিটার্ন টাইপের নয়, এটি অনুরোধে রয়েছে।
কনরাড রুডলফ

1
@ কোয়েটজলকোটল: এর অভ্যন্তরীণ decltypeঅংশটি একটি মূল্যহীন প্রেক্ষাপট, সুতরাং সদস্য ফাংশনটি আহ্বান করা সমস্যা নয় (এটি চেষ্টা করা হবে না)
স্বল্পতা রেস

1
@ টমকনাপেন ঝাঁকুনির সাথে চেষ্টা করে দেখুন এবং এটি ব্যর্থ হবে। জিসিসি এটি স্বীকার করে নিয়েছে যে বিষয়টি আমি যতদূর অবগত আছি ত্রুটি।

4
এফডাব্লুআইডাব্লু, অ-স্থিতিশীল ডেটা সদস্য struct S { int i; typedef decltype(i) Int; };হলেও কাজ করে i। এটি কাজ করে কারণ decltypeএকটি বিশেষ ব্যতিক্রম রয়েছে যেখানে একটি সাধারণ নামকে অভিব্যক্তি হিসাবে মূল্যায়ন করা হয় না। তবে আমি এই সম্ভাবনাটি কোনওভাবেই প্রশ্নের উত্তর দেওয়ার উপায় হিসাবে ভাবতে পারি না।

21

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

#define DEFINE_SELF() \
    typedef auto _self_fn() -> decltype(*this); \
    using self = decltype(((_self_fn*)0)())

struct Foo {
    DEFINE_SELF();
};

struct Bar {
    DEFINE_SELF();
};

দুর্ভাগ্যক্রমে, স্ট্যান্ডার্ডটির একটি কঠোর পঠন বলে যে এটি এমনকি বৈধ নয়। কী ঝাঁকুনি দেয় তা চেক যা thisস্থির সদস্য ফাংশনের সংজ্ঞায় ব্যবহৃত হয় না। এবং এখানে, এটি আসলে না। কোনও thisধরণের ফাংশন নির্বিশেষে ট্রেইলিং-রিটার্ন-টাইপ ব্যবহার করা হয়েছে কিনা তা জিসিসির আপত্তি নেই it এটি staticসদস্য সদস্যদের জন্য এমনকি এটির অনুমতি দেয় । তবে, স্ট্যান্ডার্ডটি আসলে যা প্রয়োজন তা হ'ল এটি thisঅ স্থিতিশীল সদস্য ফাংশন (বা নন-স্ট্যাটিক ডেটা মেম্বার ইনিশিয়ালিসার) এর সংজ্ঞার বাইরে ব্যবহার করা হয় না। ইন্টেল এটি সঠিকভাবে পায় এবং এটি প্রত্যাখ্যান করে।

দেত্তয়া আছে:

  • this কেবলমাত্র অ-স্থিতিশীল ডেটা মেম্বার ইনিশিয়ালিজার এবং অ স্থিতিশীল সদস্য ফাংশনগুলিতে ([expr.prim.general] p5) অনুমোদিত,
  • অ-স্থিতিশীল ডেটা সদস্যদের প্রারম্ভিক ([dcl.spec.auto] p5) থেকে তাদের প্রকারটি বাদ দেওয়া যাবে না,
  • অ স্থিতিশীল সদস্য ফাংশন কেবলমাত্র একটি ফাংশন কল প্রসঙ্গে একটি অযোগ্য নাম দ্বারা উল্লেখ করা যেতে পারে ([expr.ref] p4)
  • অ স্থিতিশীল সদস্য ফাংশনগুলি কেবল অযোগ্য নাম দ্বারা ডাকা যেতে পারে, এমনকি অমূল্য প্রসঙ্গেও যখন thisব্যবহার করা যায় ([over.call.func] p3),
  • যোগ্য নাম বা সদস্য অ্যাক্সেস দ্বারা অ স্থিতিশীল সদস্য ফাংশনের একটি রেফারেন্স সংজ্ঞাযুক্ত ধরণের একটি রেফারেন্স প্রয়োজন

আমি মনে করি আমি সিদ্ধান্তে বলতে পারি যে selfকোনওভাবে, কোথাও, টাইপের নাম অন্তর্ভুক্ত না করে বাস্তবায়নের কোনও উপায় নেই ।

সম্পাদনা : আমার আগের যুক্তিতে ত্রুটি আছে। "অ স্থিতিশীল সদস্য ফাংশনগুলি কেবল অযোগ্য নামেই ডাকা যায়, এমনকি অমূল্য প্রসঙ্গেও, যখন এটি ব্যবহার করা যায় ([over.call.func] p3)," ভুল। এটি আসলে যা বলে তা হ'ল

কীওয়ার্ডটি যদি this(9.3.2) স্কোপে থাকে এবং শ্রেণি T, বা এর উদ্ভূত শ্রেণীর উল্লেখ করে Tতবে অন্তর্নিহিত অবজেক্ট আর্গুমেন্টটি (*this)। যদি কীওয়ার্ডটি thisসুযোগে না থাকে বা অন্য কোনও শ্রেণিকে উল্লেখ করে তবে প্রকারের একটি স্বীকৃত অবজেক্টটি Tঅন্তর্নিহিত অবজেক্ট আর্গুমেন্টে পরিণত হয়। যদি যুক্তি তালিকার কোনও অবৈধ অবজেক্টের সাহায্যে সংযোজন করা হয় এবং ওভারলোড রেজোলিউশন এর অ স্থিতিশীল সদস্য ফাংশনগুলির মধ্যে একটি নির্বাচন করে T, কলটি দুর্গঠিত।

স্থির সদস্য ফাংশনের অভ্যন্তরে thisউপস্থিত নাও হতে পারে তবে এটি এখনও বিদ্যমান exists

যাইহোক, মন্তব্য প্রতি একটি স্ট্যাটিক সদস্য ফাংশন ভিতরে, রূপান্তরের f()জন্য (*this).f()সঞ্চালিত হবে না, এবং এটি সঞ্চালিত হয় না যে, তারপরে [expr.call] P1 লঙ্ঘন হয়:

[...] সদস্য ফাংশন কলের জন্য, পোস্টফিক্স এক্সপ্রেশন একটি অন্তর্নিহিত (9.3.1, 9.4) বা সুস্পষ্ট শ্রেণীর সদস্য অ্যাক্সেস (5.2.5) যার [...]

যেহেতু কোনও সদস্যের অ্যাক্সেস থাকবে না। এমনকি এটি কাজ করবে না।


আমি মনে করি [class.mfct.non-static] / 3 বলে যে _self_fn_1()"রূপান্তরিত" হয়েছে (*this)._self_fn_1()। যদিও এটি এটিকে অবৈধ করে তোলে কিনা তা নিশ্চিত নয়।
ডায়াপ

@ ডিআইপি এটি বলছে "ক্লাসের সদস্য হিসাবে Xএমন একটি প্রসঙ্গে thisব্যবহার করা যেতে পারে যেখানে ব্যবহার করা যেতে পারে", সুতরাং আমি মনে করি না যে রূপান্তরটি সম্পাদিত হয়েছে।

1
তবে তারপরেও তা কোনও অন্তর্নিহিত বা সুস্পষ্ট শ্রেণীর সদস্য অ্যাক্সেস নয় ..? [expr.call] / 1 "সদস্য ফাংশন কলের জন্য, পোস্টফিক্স এক্সপ্রেশন একটি অন্তর্নিহিত বা স্পষ্ট শ্রেণীর সদস্য অ্যাক্সেস হতে পারে [...]"
ডাইপ

(আমি বলতে চাইছি, আপনার সাথে কী হয় auto _self_fn_1() -> decltype(*this); auto _self_fn_1() const -> decltype(*this);?)
ডায়াপ

@dyp [expr.call] / 1 একটি ভাল পয়েন্ট, আমাকে আরও ঘুরে দেখুন। constওভারলোড সম্পর্কে , যদিও: এটি কোনও সমস্যা নয়। 5.1p3 বিশেষভাবে রুপান্তরিত করা হয়েছে এছাড়াও স্ট্যাটিক সদস্য ফাংশন প্রযোজ্য এবং বলে ধরণ thisহয় Foo*/ Bar*(ছাড়া constকোন না থাকায়,) constএর ঘোষণায় _self_fn_2

17
#define SELF_CHECK( SELF ) void self_check() { static_assert( std::is_same< typename std::decay<decltype(*this)>::type, SELF >::value, "self wrong type" ); }
#define SELF(T) typedef T self; SELF_CHECK(T)

struct Foo {
  SELF(Foo); // works, self is defined as `Foo`
};
struct Bar {
  SELF(Foo); // fails
};

এটি টেমপ্লেট ধরণের ক্ষেত্রে কাজ করে না, যেমনটি self_checkবলা হয় না, সুতরাং এটি static_assertমূল্যায়ন করা হয় না।

এটি templateএর জন্য কাজ করার জন্য আমরা কিছু হ্যাক করতে পারি , তবে এটির জন্য সামান্য রান ব্যয়ও রয়েছে।

#define TESTER_HELPER_TYPE \
template<typename T, std::size_t line> \
struct line_tester_t { \
  line_tester_t() { \
    static_assert( std::is_same< decltype(T::line_tester), line_tester_t<T,line> >::value, "test failed" ); \
    static_assert( std::is_same< decltype(&T::static_test_zzz), T*(*)() >::value, "test 2 failed" ); \
  } \
}

#define SELF_CHECK( SELF ) void self_check() { static_assert( std::is_same< typename std::decay<decltype(*this)>::type, SELF >::value, "self wrong type" ); }

#define SELF(T) typedef T self; SELF_CHECK(T); static T* static_test_zzz() { return nullptr; }; TESTER_HELPER_TYPE; line_tester_t<T,__LINE__> line_tester

structআপনার ক্লাসে খালি 1 বাইটের আকার তৈরি হয়। যদি আপনার প্রকারটি তাত্ক্ষণিক selfহয় তবে তার বিরুদ্ধে পরীক্ষা করা হয়।


সেটাও খারাপ নয়!
অরবিটে

templateক্লাস সমর্থন বিকল্পের সাথে এখনই লাইটনেসেসেসিনব্রিট করুন।
ইয়াক্ক - অ্যাডাম নেভ্রামুমন্ট

গতকাল আমি যখন কাজ ছেড়ে যাচ্ছিলাম তখন আমি এই সম্পর্কে ঠিক ভাবছিলাম। তুমি আমাকে এটা দ্বারা মেরেছ :). আমি লিঙ্কযুক্ত সমস্যাগুলি এড়াতে স্বতন্ত্র-পরীক্ষা ()টিকে ইনলাইন হিসাবে ঘোষণা করার পরামর্শ দিই (একই প্রতীক Foo :: self_check () একাধিক অবজেক্ট ফাইলে পাওয়া গেছে)।
সোয়াইন

1
@ থেসউইন: ৯.৩ / ২ হ'ল সি ++ স্ট্যান্ডার্ডের একটি অনুচ্ছেদের সূচক, যা গ্যারান্টি দেয় যে শ্রেণীর সদস্যের কার্যগুলি বর্গের সংজ্ঞার শরীরে সংজ্ঞায়িত করা হয়েছে ইতিমধ্যে, অন্তর্নিহিত inline,। তার মানে আপনার মোটেও লেখার দরকার নেই inline। সুতরাং যদি আপনি inlineআপনার পুরো ক্যারিয়ারের জন্য এই জাতীয় শ্রেণীর প্রতিটি সদস্যের ফাংশন সংজ্ঞার সামনে লিখতে থাকেন তবে আপনি এখনই থামতে পারেন;)
অরবিট

2
@ লাইটনেসেসেসিনআরবিট ওহ, আসলে আমি ছিলাম। আপনাকে ধন্যবাদ, এটি ভবিষ্যতে কিছু টাইপিং সংরক্ষণ করবে :)। আমি সি ++ সম্পর্কে কতটা জানি না তা দেখে আমি সর্বদা অবাক হয়েছি।
সোয়াইন

11

আমি এটিও অসম্ভব বলে মনে করি, এখানে অন্য একটি ব্যর্থ হয়েছে তবে আইএমএইচওর একটি আকর্ষণীয় প্রচেষ্টা যা this-অ্যাক্সেস এড়িয়ে চলে :

template<typename T>
struct class_t;

template<typename T, typename R>
struct class_t< R (T::*)() > { using type = T; };

struct Foo
{
   void self_f(); using self = typename class_t<decltype(&self_f)>::type;
};

#include <type_traits>

int main()
{
    static_assert( std::is_same< Foo::self, Foo >::value, "" );
}

যা ব্যর্থ হয়েছে কারণ সি ++ এর জন্য আপনি self_fযখন এই ঠিকানাটি নিতে চান তখন আপনাকে ক্লাসের সাথে যোগ্যতা অর্জন করতে হবে :(


এবং একই সমস্যা int T::*সদস্য ভেরিয়েবলের নিয়মিত পয়েন্টার সহ ঘটে । এবং int self_var; typedef decltype(&self_var) self_ptrকাজ করে না, এটি কেবল একটি নিয়মিত int*
এমসাল্টাররা 16:54

9

আমি সম্প্রতি আবিষ্কার করেছি যা *thisএকটি ব্রেস-বা-সমান-আরম্ভকারী হিসাবে অনুমোদিত । (§ 5.1.1 বর্ণিত থেকে n3337 কাজ খসড়া ):

3 [..] অন্যান্য প্রসঙ্গে বিষয়বস্তুর মত প্রকাশের বিপরীতে *this, সদস্য ফাংশন বডিটির বাইরে শ্রেণি সদস্য অ্যাক্সেসের জন্য (5.2.5) সম্পূর্ণ ধরণের হতে হবে না। [..]

4 অন্যথায়, যদি কোনও সদস্য-ঘোষণাকারীthis একটি দশম শ্রেণীর স্থিতিশীল ডেটা সদস্য (9.2) ঘোষণা করে তবে এক্সপ্রেশনটি বিকল্প প্যাকেজ -বা সম-ইনিশিয়ালাইজারের মধ্যে "পয়েন্টার টু এক্স" প্রকারের একটি মূল্য । এটি সদস্য-ঘোষণাকারীর অন্য কোথাও উপস্থিত হবে না ।

5 প্রকাশটি thisঅন্য কোনও প্রসঙ্গে উপস্থিত হবে না। [ উদাহরণ:

class Outer {
    int a[sizeof(*this)];               // error: not inside a member function
    unsigned int sz = sizeof(*this);    // OK: in brace-or-equal-initializer

    void f() {
        int b[sizeof(*this)];           // OK
        struct Inner {
            int c[sizeof(*this)];       // error: not inside a member function of Inner
        };
    }
};

- শেষ উদাহরণ ]

এটি মাথায় রেখে, নিম্নলিখিত কোড:

struct Foo
{
    Foo* test = this;
    using self = decltype(test);

    static void smf()
    {
        self foo;
    }
};

#include <iostream>
#include <type_traits>

int main()
{
    static_assert( std::is_same< Foo::self, Foo* >::value, "" );
}

ড্যানিয়েল ফ্রে এর পাস static_assert

Live example


আপনার কাছে testযদিও বিরক্তিকর অব্যবহৃত পরিবর্তনশীল রয়েছে
এমএম

@ ম্যাট সত্য, তবে আমি এটি আকর্ষণীয় মনে করেছি।

1
এটি ছাড়া কাজ করতে পারে = this, তাই না? এবং কেন কেবল নয়using self = Foo*;
ব্যবহারকারীর 2515

1
আমরা অবশ্যই এখানে কিছু অর্জন করতে পারি নি, কারণ আমাদের হ'ল ঘোষণা testকরতে হয়েছিল, হুম Foo *!
পল স্যান্ডার্স

4

যদি না টাইপ পরিক্ষেপ ক্লাসের সদস্য টাইপ করা প্রয়োজন আপনি ব্যবহার প্রতিস্থাপন করতে পারে selfসঙ্গে decltype(*this)। আপনি যদি নিজের কোডটিতে এটি অনেক জায়গায় ব্যবহার করেন তবে আপনি ম্যাক্রোটিকে SELFনিম্নলিখিত হিসাবে সংজ্ঞায়িত করতে পারেন :

#define SELF decltype(*this)

2
এবং আপনি এটি ক্লাসের বাইরে বা নেস্টেড ক্লাসগুলিতে ব্যবহার করতে পারবেন না
ড্রাক্স

1
@ ড্রাক্স: এটি ক্লাসের বাইরে উপলব্ধ থাকার কথা নয়।
বেন ভয়েগট

@ বেনভয়েট তবে এটি নেস্টেড ক্লাসে উপলব্ধ বলে মনে করা হচ্ছে যা আইএমও সবচেয়ে আকর্ষণীয় ব্যবহারের ক্ষেত্রে।
ড্রাক্স

1
আমি তাই মনে করি না. selfতাত্ক্ষণিকভাবে ঘেরযুক্ত ক্লাসটি উল্লেখ করা উচিত এবং বাইরের শ্রেণিটি নয়? তবে আমি পিএইচপি জানি না।
বেন ভয়েগট

1
@ লাইটনেসেসেসিনআরবিট: আমি অনুমান করি যে কোড এবং ত্রুটিটি "পিএইচপিতে নেস্টেড ধরণের নেই" বলে মনে করা হচ্ছে?
বেন ভয়েগট

1

আমার সংস্করণ সরবরাহ করুন। সবচেয়ে ভাল বিষয়টি হল এর ব্যবহারটি স্থানীয় শ্রেণীর মতো। তবে এটি টেম্পলেট শ্রেণীর জন্য কাজ করে না।

template<class T> class Self;

#define CLASS(Name) \
class Name##_; \
typedef Self<Name##_> Name; \
template<> class Self<Name##_>

CLASS(A)
{
    int i;
    Self* clone() const { return new Self(*this); }
};

CLASS(B) : public A
{
    float f;
    Self* clone() const { return new Self(*this); }
};

1

এইচডিডি দ্বারা উত্তরের উপর ভিত্তি করে, আমি দেখতে পেলাম যে একমাত্র জিনিসটি অনুপস্থিত ছিল তা হল রেফারেন্সটি মুছে ফেলা, সেজন্য std :: is_same চেক ব্যর্থ হয়েছে (খ / সি ফলাফলের ধরণটি আসলে টাইপের একটি রেফারেন্স)। এখন এই প্যারামিটার-কম ম্যাক্রো সমস্ত কাজ করতে পারে। নীচের কাজের উদাহরণ (আমি জিসিসি 8.1.1 ব্যবহার করি)।

#define DEFINE_SELF \
    typedef auto _self_fn() -> std::remove_reference<decltype(*this)>::type; \
    using self = decltype(((_self_fn*)0)())

class A {
    public:
    DEFINE_SELF;
};

int main()
{
    if (std::is_same_v<A::self, A>)
        std::cout << "is A";
}

এটি জিসিসি ব্যতীত অন্য সংকলকগুলিতে সংকলন করে না।
zedu

0

আমি "এটি নিজেই করা" এর সুস্পষ্ট সমাধানটির পুনরাবৃত্তি করব। এটি কোডটির সংযোগ সি ++ 11 সংস্করণ, যা সাধারণ ক্লাস এবং শ্রেণি টেম্পলেট উভয়ই নিয়ে কাজ করে:

#define DECLARE_SELF(Type) \
    typedef Type TySelf; /**< @brief type of this class */ \
    /** checks the consistency of TySelf type (calling it has no effect) */ \
    void self_check() \
    { \
        static_assert(std::is_same<decltype(*((TySelf*)(0))), \
            decltype(*this)>::value, "TySelf is not what it should be"); \
    } \
    enum { static_self_check_token = __LINE__ }; \
    static_assert(int(static_self_check_token) == \
        int(TySelf::static_self_check_token), \
        "TySelf is not what it should be")

আপনি এটি আদর্শে ক্রিয়াতে দেখতে পারেন । উত্সাহ, এই ফলাফলের দিকে পরিচালিত নীচে:

#define DECLARE_SELF(Type) typedef Type _TySelf; /**< @brief type of this class */

struct XYZ {
    DECLARE_SELF(XYZ)
};

কোডটি অন্য কোনও ক্লাসে কপি-পেস্ট করা এবং এক্সওয়াইজেড পরিবর্তন করতে ভুলে এখানে স্পষ্ট সমস্যা রয়েছে:

struct ABC {
    DECLARE_SELF(XYZ) // !!
};

আমার প্রথম পদ্ধতির খুব আসল ছিল না - একটি ফাংশন তৈরি করে:

/**
 *  @brief namespace for checking the _TySelf type consistency
 */
namespace __self {

/**
 *  @brief compile-time assertion (_TySelf must be declared the same as the type of class)
 *
 *  @tparam _TySelf is reported self type
 *  @tparam _TyDecltypeThis is type of <tt>*this</tt>
 */
template <class _TySelf, class _TyDecltypeThis>
class CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE;

/**
 *  @brief compile-time assertion (specialization for assertion passing)
 *  @tparam _TySelf is reported self type (same as type of <tt>*this</tt>)
 */
template <class _TySelf>
class CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE<_TySelf, _TySelf> {};

/**
 *  @brief static assertion helper type
 *  @tparam n_size is size of object being used as assertion message
 *      (if it's a incomplete type, compiler will display object name in error output)
 */
template <const size_t n_size>
class CStaticAssert {};

/**
 *  @brief helper function for self-check, this is used to derive type of this
 *      in absence of <tt>decltype()</tt> in older versions of C++
 *
 *  @tparam _TyA is reported self type
 *  @tparam _TyB is type of <tt>*this</tt>
 */
template <class _TyA, class _TyB>
inline void __self_check_helper(_TyB *UNUSED(p_this))
{
    typedef CStaticAssert<sizeof(CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE<_TyA, _TyB>)> _TyAssert;
    // make sure that the type reported as self and type of *this is the same
}

/**
 *  @def __SELF_CHECK
 *  @brief declares the body of __self_check() function
 */
#define __SELF_CHECK \
    /** checks the consistency of _TySelf type (calling it has no effect) */ \
    inline void __self_check() \
    { \
        __self::__self_check_helper<_TySelf>(this); \
    }

/**
 *  @def DECLARE_SELF
 *  @brief declares _TySelf type and adds code to make sure that it is indeed a correct one
 *  @param[in] Type is type of the enclosing class
 */
#define DECLARE_SELF(Type) \
    typedef Type _TySelf; /**< @brief type of this class */ \
    __SELF_CHECK

} // ~self

এটি একপ্রকার দীর্ঘ, তবে দয়া করে এখানে আমাকে সহ্য করুন। এতে সি ++ 03 ছাড়াই কাজ করার সুবিধা রয়েছে decltypeকারণ __self_check_helperফাংশনটি ধরণের ধরণের নিযুক্ত করার জন্য নিযুক্ত করা হয় this। এছাড়াও, নেই static_assert, তবে sizeof()কৌশলটি পরিবর্তে নিযুক্ত করা হয়েছে। আপনি এটি C ++ 0x এর জন্য আরও খাটো করতে পারেন। এখন এটি টেমপ্লেটগুলির জন্য কাজ করবে না। এছাড়াও, ম্যাক্রোটি শেষদিকে সেমিকোলনের প্রত্যাশা না করে একটি ছোটখাটো সমস্যা রয়েছে, পেডেন্টিকের সাথে সংকলন করা হলে এটি একটি অতিরিক্ত অপ্রয়োজনীয় সেমিকোলন সম্পর্কে অভিযোগ করবে (বা আপনি একটি অদ্ভুত দেখাচ্ছে ম্যাক্রোটি ছেড়ে দেবেন এবং এর শরীরে সেমিকোলনে শেষ হবে না XYZand ABC)।

Typeযেটি পাস হয়েছে তাতে একটি চেক করা DECLARE_SELFকোনও বিকল্প নয়, কারণ এটি কেবল XYZক্লাসটি পরীক্ষা করবে (যা ঠিক আছে), ABC(যা ত্রুটি রয়েছে) থেকে অবজ্ঞাত । অতঃপর এটা আমাকে আঘাত করল। টেমপ্লেটগুলির সাথে কাজ করে এমন কোনও অতিরিক্ত সঞ্চয়স্থান শূন্য-ব্যতীত সমাধান:

namespace __self {

/**
 *  @brief compile-time assertion (_TySelf must be declared the same as the type of class)
 *  @tparam b_check is the asserted value
 */
template <bool b_check>
class CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE2;

/**
 *  @brief compile-time assertion (specialization for assertion passing)
 */
template <>
class CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE2<true> {};

/**
 *  @def DECLARE_SELF
 *  @brief declares _TySelf type and adds code to make sure that it is indeed a correct one
 *  @param[in] Type is type of the enclosing class
 */
#define DECLARE_SELF(Type) \
    typedef Type _TySelf; /**< @brief type of this class */ \
    __SELF_CHECK \
    enum { __static_self_check_token = __LINE__ }; \
    typedef __self::CStaticAssert<sizeof(CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE2<int(__static_self_check_token) == int(_TySelf::__static_self_check_token)>)> __static_self_check

} // ~__self 

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

আমাকে একটি ভাল অনুপ্রেরণা দেওয়ার জন্য আমি ইয়াককে ধন্যবাদ জানাতে চাই। আমি প্রথমে তার উত্তর না দেখে এটি লিখব না।

ভিএস ২০০৮ এবং জি ++ ৪.6.৩ এর সাথে পরীক্ষিত। প্রকৃতপক্ষে, XYZএবং ABCউদাহরণ সহ, এটি অভিযোগ করে:

ipolok@ivs:~$ g++ self.cpp -c -o self.o
self.cpp:91:5: error: invalid application of âsizeofâ to incomplete type â__self::CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE2<false>â
self.cpp:91:5: error: template argument 1 is invalid
self.cpp: In function âvoid __self::__self_check_helper(_TyB*) [with _TyA = XYZ, _TyB = ABC]â:
self.cpp:91:5:   instantiated from here
self.cpp:58:87: error: invalid application of âsizeofâ to incomplete type â__self::CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE<XYZ, ABC

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

template <class X>
struct ABC {
    DECLARE_SELF(XYZ); // line 92
};

int main(int argc, char **argv)
{
    ABC<int> abc;
    return 0;
}

আমরা পাব:

ipolok@ivs:~$ g++ self.cpp -c -o self.o
self.cpp: In instantiation of âABC<int>â:
self.cpp:97:18:   instantiated from here
self.cpp:92:9: error: invalid application of âsizeofâ to incomplete type â__self::CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE2<false>â

কেবলমাত্র লাইন-নম্বর চেকটি ট্রিগার করেছিল, কারণ ফাংশন চেকটি সংকলিত হয়নি (আশানুরূপ হিসাবে)।

সি ++ 0x (এবং অশুভ আন্ডারস্কোর ছাড়াই) দিয়ে আপনার কেবল প্রয়োজন হবে:

namespace self_util {

/**
 *  @brief compile-time assertion (tokens in class and TySelf must match)
 *  @tparam b_check is the asserted value
 */
template <bool b_check>
class SELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE;

/**
 *  @brief compile-time assertion (specialization for assertion passing)
 */
template <>
class SELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE<true> {};

/**
 *  @brief static assertion helper type
 *  @tparam n_size is size of object being used as assertion message
 *      (if it's a incomplete type, compiler will display object name in error output)
 */
template <const size_t n_size>
class CStaticAssert {};

#define SELF_CHECK \
    /** checks the consistency of TySelf type (calling it has no effect) */ \
    void self_check() \
    { \
        static_assert(std::is_same<TySelf, decltype(*this)>::value, "TySelf is not what it should be"); \
    }

#define DECLARE_SELF(Type) \
    typedef Type TySelf; /**< @brief type of this class */ \
    SELF_CHECK \
    enum { static_self_check_token = __LINE__ }; \
    typedef self_util::CStaticAssert<sizeof(SELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE<int(static_self_check_token) == int(TySelf::static_self_check_token)>)> static_self_check

} // ~self_util

আমি বিশ্বাস করি যে CStaticAssert বিটটি দুঃখজনকভাবে এখনও প্রয়োজনীয় কারণ এটি একটি প্রকার তৈরি করে, যা টেম্পলেট বডিতে টাইপডেফ-এড হয় (আমি মনে করি এটি দিয়ে সম্পন্ন করা যায় না static_assert)। এই পদ্ধতির সুবিধাটি এখনও এটির শূন্য ব্যয়।


আপনি static_assertএখানে মূলত পুনরায় বাস্তবায়ন করছেন , তাই না? তদতিরিক্ত, আপনার সম্পূর্ণ কোডটি অবৈধ কারণ আপনি অবৈধ (সংরক্ষিত) শনাক্তকারী ব্যবহার করছেন।
কনরাড রুডল্ফ

@ কনরাড রুডল্ফ হ্যাঁ, বাস্তবে এটিই ঘটেছে। আমার কাছে সেই সময় সি ++ 0x নেই, তাই আমি সম্পূর্ণ উত্তর সরবরাহের জন্য স্ট্যাটিক_সেটারে পুনরায় প্রয়োগ করলাম। আমি উত্তরে বলেছি। এটা কি অবৈধ? আপনি কীভাবে নির্দেশ করতে পারেন? এটি সূক্ষ্মভাবে সংকলিত হয়েছে, আমি এখনই এটি ব্যবহার করছি।
সোয়াইন

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

@ কনরাড রুডল্ফ আমি দেখছি, আমি এটি জানতাম না। আমার কাছে প্রচুর কোড রয়েছে যা ব্যবহার করে, লিনাক্স / ম্যাক / উইন্ডোজ উভয় ক্ষেত্রেই এর সাথে কখনও সমস্যা ছিল না। তবে আমার ধারণা এটি জেনে রাখা ভাল।
সোয়াইন

0

আমি এই ন্যাকামি টেম্পলেটগুলি সম্পর্কে জানি না, অতি-সাধারণ কিছু সম্পর্কে কীভাবে:

#define DECLARE_TYPEOF_THIS typedef CLASSNAME typeof_this
#define ANNOTATED_CLASSNAME(DUMMY) CLASSNAME

#define CLASSNAME X
class ANNOTATED_CLASSNAME (X)
{
public:
    DECLARE_TYPEOF_THIS;
    CLASSNAME () { moi = this; }
    ~CLASSNAME () { }
    typeof_this *moi;
    // ...
};    
#undef CLASSNAME

#define CLASSNAME Y
class ANNOTATED_CLASSNAME (Y)
{
    // ...
};
#undef CLASSNAME

কাজ শেষ হয়েছে, যদি না আপনি কয়েকটা ম্যাক্রো দাঁড়াতে পারেন। এমনকি আপনি CLASSNAMEআপনার নির্মাতাকে (এবং অবশ্যই, ডেস্ট্রাক্টর) ঘোষণার জন্য ব্যবহার করতে পারেন ।

লাইভ ডেমো


1
ক্লাসটি কীভাবে ব্যবহার করা যায় / তার পরে অবশ্যই ব্যবহার করা যায় তার উপর এটি বেশ স্পষ্টভাবে প্রভাব ফেলেছে
লাইটনেস রেস ইন অরবিট

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