কোনও শ্রেণীর প্রদত্ত স্বাক্ষরের সদস্য ফাংশন রয়েছে কিনা তা পরীক্ষা করুন


135

কোনও শ্রেণীর প্রদত্ত স্বাক্ষরের নির্দিষ্ট সদস্য ফাংশন রয়েছে কিনা তা সনাক্ত করার জন্য আমি একটি টেম্পলেট কৌশল জিজ্ঞাসা করছি।

সমস্যাটি এখানে যেমন উদ্ধৃত হয়েছে তার অনুরূপ: http://www.gotw.ca/gotw/071.htm তবে একই নয়: সুতারের বইয়ের আইটেমে তিনি এই প্রশ্নের উত্তর দিয়েছিলেন যে একটি শ্রেণি সি একটি সদস্যকে ফাংশন সরবরাহ করতে হবে একটি নির্দিষ্ট স্বাক্ষর, অন্যথায় প্রোগ্রাম সংকলন করবে না। আমার সমস্যায় কোনও শ্রেণীর যদি সেই ফাংশন থাকে তবে আমার কিছু করা দরকার, অন্যথায় "অন্য কিছু" করুন।

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

আমি দুটি কারণের জন্য সমাধানটি পছন্দ করি না:

  1. অনুপ্রবেশকারী হতে না পারার জন্য আপনাকে অবশ্যই বিশ্বব্যাপী "সিরিয়ালাইজ" ফাংশনটি ওভাররাইড করতে হবে যা উত্সাহিত :: সিরিয়ালাইজেশন নেমস্পেসে রয়েছে, সুতরাং আপনার নিজের ক্লায়েন্ট কোডে নেমস্পেস বুস্ট এবং নেমস্পেস সিরিয়ালাইজেশন খুলতে হবে!
  2. এই জগাখিরি সমাধান করার স্ট্যাকটি ছিল 10 থেকে 12 ফাংশন আমন্ত্রণ।

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

আপনি এই ধাঁধাটি সমাধান করার জন্য আমাকে একটি ইঙ্গিত দিতে পারেন?



@ আর.মার্টিনহো ফার্নান্দেস আপনি কী ধরণের উত্তর খুঁজছেন? মাইক কিংহানের এই উত্তরটি বেশ গভীরতায় চলেছে এবং সি ++ 11 স্টাফ ব্যবহার করছে।
jrok

@ আর.মার্টিনহো ফার্নান্দেস সম্ভবত এটি আপনি খুঁজছেন এমন আধুনিক সংস্করণ?
ড্যানিয়েল ফ্রে

উত্তর:


90

আমি আপনাকে সঠিকভাবে বুঝতে পেরেছি কিনা তা নিশ্চিত নই, তবে আপনি সংকলন সময়ে ফাংশন উপস্থিতি সনাক্ত করতে SFINAE ব্যবহার করতে পারেন। আমার কোড থেকে উদাহরণ (ক্লাসে সদস্য ফাংশন আকার_ত ব্যবহৃত_মেমরি () কনস্ট) রয়েছে কিনা পরীক্ষা করে।

template<typename T>
struct HasUsedMemoryMethod
{
    template<typename U, size_t (U::*)() const> struct SFINAE {};
    template<typename U> static char Test(SFINAE<U, &U::used_memory>*);
    template<typename U> static int Test(...);
    static const bool Has = sizeof(Test<T>(0)) == sizeof(char);
};

template<typename TMap>
void ReportMemUsage(const TMap& m, std::true_type)
{
        // We may call used_memory() on m here.
}
template<typename TMap>
void ReportMemUsage(const TMap&, std::false_type)
{
}
template<typename TMap>
void ReportMemUsage(const TMap& m)
{
    ReportMemUsage(m, 
        std::integral_constant<bool, HasUsedMemoryMethod<TMap>::Has>());
}

14
এটা কি ??? এটি কি আইনী সি ++ কোড ?? আপনি কি "টেমপ্লেট <টি টাইপনাম ইউ, আকার_টি (ইউ :: *) () কনস্টন্ট>" লিখতে পারেন? তবে ... এটি একটি দুর্দান্ত এবং নতুন সমাধান! আমি আপনাকে ধন্যবাদ, আমি আমার কলেজগুলির সাথে আগামীকাল আরও ভাল বিশ্লেষণ করব ... দুর্দান্ত!
ugasoft

2
উদাহরণটি 'int_to_type' সংজ্ঞাটি অনুপস্থিত। স্পষ্টতই এটির উত্তর যুক্ত হয় না, তবে এর অর্থ হ'ল দ্রুত কাট & পেস্টের পরে লোকেরা আপনার কোডটিকে কার্যত দেখতে পাবে।
রিচার্ড কর্ডেন

2
Int_to_type এর একটি সাধারণ সংজ্ঞা হতে পারে: 'টেমপ্লেট << এন> <<<< int_to_type {};'। অনেকগুলি বাস্তবায়ন প্যারামিটার এন মানকে হয় এনাম বা অন্য কোনও স্থিতিশীল পূর্ণসংখ্যার ধ্রুবক হিসাবে রাখে (টেমপ্লেট << এন> <<<< int_to_type {enum {মান = N};}; / টেমপ্লেট << এন> <<<< int_to_type {স্ট্যাটিক কনট ইনট মান = এন;})
ডেভিড রদ্রিগেজ - ড্রিবিস

2
কেবলমাত্র ইনস্ট_ টু টাইপের পরিবর্তে বগস্ট :: ইন্টিগ্রাল_ কনস্ট্যান্ট নিন।
ভাদিম ফের্ডেরার

2
@ জোহানলুন্ডবার্গ এটি পয়েন্টার-টু- (অ স্থির-) সদস্য ফাংশন। উদাহরণস্বরূপ size_t(std::vector::*p)() = &std::vector::size;,।
মনিকা

133

C ++ 11 বৈশিষ্ট্যগুলির উপর নির্ভর করে এখানে একটি সম্ভাব্য বাস্তবায়ন রয়েছে। এটি উত্তরাধিকার সূত্রে প্রাপ্ত হলেও সঠিকভাবে ফাংশনটি সনাক্ত করে (মাইকের কিংহান তার উত্তরে যেমনটি গৃহীত উত্তরের সমাধানের বিপরীতে )।

এই স্নিপেট পরীক্ষার জন্য যে ফাংশনটি বলা হয় তাকে বলা হয় serialize:

#include <type_traits>

// Primary template with a static assertion
// for a meaningful error message
// if it ever gets instantiated.
// We could leave it undefined if we didn't care.

template<typename, typename T>
struct has_serialize {
    static_assert(
        std::integral_constant<T, false>::value,
        "Second template parameter needs to be of function type.");
};

// specialization that does the checking

template<typename C, typename Ret, typename... Args>
struct has_serialize<C, Ret(Args...)> {
private:
    template<typename T>
    static constexpr auto check(T*)
    -> typename
        std::is_same<
            decltype( std::declval<T>().serialize( std::declval<Args>()... ) ),
            Ret    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        >::type;  // attempt to call it and see if the return type is correct

    template<typename>
    static constexpr std::false_type check(...);

    typedef decltype(check<C>(0)) type;

public:
    static constexpr bool value = type::value;
};

ব্যবহার:

struct X {
     int serialize(const std::string&) { return 42; } 
};

struct Y : X {};

std::cout << has_serialize<Y, int(const std::string&)>::value; // will print 1

ওয়াইয়ের "সিরিয়ালাইজ" নামক কোনও পদ্ধতি না থাকলে এই কাজ করে? "সিরিয়ালাইজড" পদ্ধতিটি উপস্থিত না থাকলে কীভাবে এটি একটি মিথ্যা মান ফিরে আসবে তা আমি দেখছি না।
কলিন

1
@ কলিন এই ক্ষেত্রে টেমপ্লেট প্যারামিটারের বিকল্পটি চেকের প্রথম ওভারলোডের জন্য ব্যর্থ হয় এবং এটি ওভারলোড সেট থেকে বাতিল করা হয়। এটি দ্বিতীয়টিতে ফিরে আসে যা মিথ্যা_প্রকার ফিরে আসে। এটি কোনও সংকলক ত্রুটি নয় কারণ SFINAE নীতি।
jrok

1
@ elios264 এখনও নেই। আপনি যা যা পরীক্ষা করতে চান তার জন্য একটি টেমপ্লেট লিখতে আপনি ম্যাক্রো ব্যবহার করতে পারেন।
jrok

1
তদন্তের জন্য যুক্তিটি টি বা টি ও টির পরিবর্তে টি * টাইপের কেন কোনও বিশেষ কারণ?
শিবুমি

1
কিন্তু যদি serializeনিজেই কোনও টেম্পলেট গ্রহণ করে। serializeসঠিক টাইপ না করে কি অস্তিত্বের জন্য পরীক্ষা করার কোনও উপায় আছে ?
হাই-এঞ্জেল

37

সংকলনের সদস্য-ফাংশন অন্তঃকরণের এই প্রশ্নের গৃহীত উত্তরের, যদিও এটি কেবলমাত্র জনপ্রিয়, তবে একটি ছিনত রয়েছে যা নিম্নলিখিত প্রোগ্রামে লক্ষ্য করা যায়:

#include <type_traits>
#include <iostream>
#include <memory>

/*  Here we apply the accepted answer's technique to probe for the
    the existence of `E T::operator*() const`
*/
template<typename T, typename E>
struct has_const_reference_op
{
    template<typename U, E (U::*)() const> struct SFINAE {};
    template<typename U> static char Test(SFINAE<U, &U::operator*>*);
    template<typename U> static int Test(...);
    static const bool value = sizeof(Test<T>(0)) == sizeof(char);
};

using namespace std;

/* Here we test the `std::` smart pointer templates, including the
    deprecated `auto_ptr<T>`, to determine in each case whether
    T = (the template instantiated for `int`) provides 
    `int & T::operator*() const` - which all of them in fact do.
*/ 
int main(void)
{
    cout << has_const_reference_op<auto_ptr<int>,int &>::value;
    cout << has_const_reference_op<unique_ptr<int>,int &>::value;
    cout << has_const_reference_op<shared_ptr<int>,int &>::value << endl;
    return 0;
}

জিসিসি 4.6.3 সঙ্গে অন্তর্নির্মিত, প্রোগ্রাম আউটপুট 110- আমাদের পরিচায়ক যে T = std::shared_ptr<int>নেই না প্রদান int & T::operator*() const

আপনি যদি ইতিমধ্যে এই গোটচা সম্পর্কে জ্ঞানী না std::shared_ptr<T>হন তবে শিরোনামের সংজ্ঞাটি একবার দেখলে <memory>আলোকপাত হবে। সেই বাস্তবায়নে, std::shared_ptr<T>এটি একটি বেস ক্লাস থেকে উত্পন্ন হয় যা থেকে এটি উত্তরাধিকার সূত্রে প্রাপ্ত হয় operator*() const। সুতরাং যে টেম্পলেট ইনস্ট্যান্টেশনটি SFINAE<U, &U::operator*>অপারেটরটির জন্য "সন্ধান" গঠন করে U = std::shared_ptr<T>তা ঘটবে না, কারণ নিজস্ব নিজস্ব std::shared_ptr<T>কোনও operator*()নেই এবং টেম্পলেট ইনস্ট্যান্টেশন "উত্তরাধিকার করে না"।

এই সদস্যটির Tকিছু সদস্যের কার্যকারিতা রয়েছে কিনা তা সনাক্ত করার জন্য "সাইজ অফ () ট্রিক" ব্যবহার করে সুপরিচিত SFINAE পদ্ধতির উপর প্রভাব ফেলবে না mf(উদাহরণস্বরূপ এই উত্তর এবং মন্তব্যগুলি দেখুন)। তবে T::mfবিদ্যমান থাকার বিষয়টি প্রায়শই (সাধারণত?) যথেষ্ট পরিমাণে ভাল হয় না: আপনার এটির একটি পছন্দসই স্বাক্ষর রয়েছে তাও স্থাপন করার প্রয়োজন হতে পারে। সেখানেই সচিত্র কৌশলগুলির স্কোর। কাঙ্ক্ষিত স্বাক্ষরের পয়েন্টারাইজড ভেরিয়েন্টটি কোনও টেম্পলেট প্রকারের প্যারামিটারে লিখিত আছে &T::mfযা SFINAE অনুসন্ধান সফল হওয়ার জন্য সন্তুষ্ট থাকতে হবে । কিন্তু T::mfউত্তরাধিকার সূত্রে প্রাপ্ত এই টেমপ্লেটটি তাত্ক্ষণিক কৌশলটি ভুল উত্তর দেয় ।

সংকলনের অন্তর্মুখীকরণের জন্য একটি নিরাপদ SFINAE কৌশল T::mfঅবশ্যই &T::mfSFINAE ফাংশন টেম্পলেট রেজোলিউশনের উপর নির্ভর করে এমন কোনও ধরণের ইনস্ট্যান্ট করতে একটি টেম্পলেট যুক্তির মধ্যে অবশ্যই এড়ানো উচিত । পরিবর্তে, SFINAE টেমপ্লেট ফাংশন রেজোলিউশন কেবল ওভারলোডেড SFINAE প্রোব ফাংশনের যুক্তির ধরণ হিসাবে ব্যবহৃত ঠিক প্রাসঙ্গিক ধরণের ঘোষণার উপর নির্ভর করতে পারে।

এই সীমাবদ্ধতা অনুসরণ করে এমন প্রশ্নের উত্তরের মাধ্যমে আমি E T::operator*() constনির্বিচারে Tএবং সংকলনের সময় সনাক্তকরণের জন্য চিত্রিত করব E। একই প্যাটার্নটি অন্য যে কোনও সদস্য পদ্ধতির স্বাক্ষরের জন্য তদন্ত করতে মুতাটিস মুটানডিস প্রয়োগ করবে ।

#include <type_traits>

/*! The template `has_const_reference_op<T,E>` exports a
    boolean constant `value that is true iff `T` provides
    `E T::operator*() const`
*/ 
template< typename T, typename E>
struct has_const_reference_op
{
    /* SFINAE operator-has-correct-sig :) */
    template<typename A>
    static std::true_type test(E (A::*)() const) {
        return std::true_type();
    }

    /* SFINAE operator-exists :) */
    template <typename A> 
    static decltype(test(&A::operator*)) 
    test(decltype(&A::operator*),void *) {
        /* Operator exists. What about sig? */
        typedef decltype(test(&A::operator*)) return_type; 
        return return_type();
    }

    /* SFINAE game over :( */
    template<typename A>
    static std::false_type test(...) {
        return std::false_type(); 
    }

    /* This will be either `std::true_type` or `std::false_type` */
    typedef decltype(test<T>(0,0)) type;

    static const bool value = type::value; /* Which is it? */
};

এই সমাধানে, ওভারলোড হওয়া SFINAE প্রোব ফাংশনটি test()"পুনরাবৃত্তভাবে পুনরায় চাওয়া হয়" is (অবশ্যই এটি মোটেও আহ্বান করা হয়নি; এটি কেবল সংকলক দ্বারা প্রত্যাবর্তনের ধরণের প্রত্যাবর্তনগুলি সমাধান করেছে))

আমাদের কমপক্ষে একটি এবং সর্বোচ্চ দুটি পয়েন্টের জন্য তদন্ত করতে হবে:

  • T::operator*()আদৌ কি আছে? যদি না হয়, আমরা সম্পন্ন করেছি।
  • প্রদত্ত যা T::operator*()বিদ্যমান, তার স্বাক্ষর E T::operator*() constকি?

আমরা একক কলের রিটার্নের ধরণের মূল্যায়ন করে উত্তরগুলি পাই test(0,0)। এটি করেছেন:

    typedef decltype(test<T>(0,0)) type;

এই কলটির /* SFINAE operator-exists :) */ওভারলোডের সমাধান হতে পারে test(), বা এটি ওভারলোডের কাছে সমাধান হতে পারে /* SFINAE game over :( */। এটি /* SFINAE operator-has-correct-sig :) */ওভারলোডের সমাধান করতে পারে না , কারণ এটি কেবল একটি যুক্তি প্রত্যাশা করে এবং আমরা দুটি পাস করছি।

আমরা কেন দুজনে পাস করছি? কেবল রেজোলিউশনকে বাদ দিতে বাধ্য করা /* SFINAE operator-has-correct-sig :) */। দ্বিতীয় যুক্তির আর কোনও ইঙ্গিত নেই।

এই কল test(0,0)থেকে ইচ্ছা সমাধানে /* SFINAE operator-exists :) */যে জমিদার, যা প্রথম প্যারামিটার প্রকার ধরো যদি প্রথম আর্গুমেন্ট 0 satifies decltype(&A::operator*)সঙ্গে A = T। 0 T::operator*উপস্থিত থাকলে সেই প্রকারটি পূরণ করবে ।

ধরা যাক সংকলকটি হ্যাঁ এর জন্য হ্যাঁ। তারপরে এটি চলছে /* SFINAE operator-exists :) */এবং এটির জন্য ফাংশন কলের রিটার্নের ধরণটি নির্ধারণ করা দরকার, যা সেই ক্ষেত্রে decltype(test(&A::operator*))- রিটার্নের ধরণে অন্য কোনও কল test()

এবার, আমরা কেবল একটি যুক্তি দিয়ে যাচ্ছি &A::operator*, যা আমরা এখন জানি যে উপস্থিত রয়েছে, বা আমরা এখানে থাকব না। একটি কল test(&A::operator*)যথাসাধ্য সমাধান হয় /* SFINAE operator-has-correct-sig :) */বা আবার করতে পারেন সমাধান করতে /* SFINAE game over :( */। কল ম্যাচ হবে /* SFINAE operator-has-correct-sig :) */ধরো যদি &A::operator*যে জমিদার, যা একক প্যারামিটার প্রকার সন্তুষ্ট E (A::*)() constসঙ্গে, A = T

সংকলকটি এখানে হ্যাঁ বলবে যদি T::operator*সেই পছন্দসই স্বাক্ষর থাকে এবং তারপরে আবার ওভারলোডের রিটার্নের ধরণের মূল্যায়ন করতে হয়। এখন আর কোনও "পুনরাবৃত্তি" নেই: এটি std::true_type

সংকলক যদি /* SFINAE operator-exists :) */কলটির জন্য test(0,0)চয়ন না করে বা কলটির /* SFINAE operator-has-correct-sig :) */জন্য চয়ন না করে test(&A::operator*), তবে উভয় ক্ষেত্রেই এটি চলে /* SFINAE game over :( */এবং চূড়ান্ত রিটার্ন টাইপ হয় std::false_type

এখানে একটি পরীক্ষা প্রোগ্রাম রয়েছে যা বিভিন্ন ধরণের মামলার প্রত্যাশিত উত্তরগুলি তৈরি করে টেমপ্লেটটি দেখায় (আবার জিসিসি 4.6.3)।

// To test
struct empty{};

// To test 
struct int_ref
{
    int & operator*() const {
        return *_pint;
    }
    int & foo() const {
        return *_pint;
    }
    int * _pint;
};

// To test 
struct sub_int_ref : int_ref{};

// To test 
template<typename E>
struct ee_ref
{
    E & operator*() {
        return *_pe;
    }
    E & foo() const {
        return *_pe;
    }
    E * _pe;
};

// To test 
struct sub_ee_ref : ee_ref<char>{};

using namespace std;

#include <iostream>
#include <memory>
#include <vector>

int main(void)
{
    cout << "Expect Yes" << endl;
    cout << has_const_reference_op<auto_ptr<int>,int &>::value;
    cout << has_const_reference_op<unique_ptr<int>,int &>::value;
    cout << has_const_reference_op<shared_ptr<int>,int &>::value;
    cout << has_const_reference_op<std::vector<int>::iterator,int &>::value;
    cout << has_const_reference_op<std::vector<int>::const_iterator,
            int const &>::value;
    cout << has_const_reference_op<int_ref,int &>::value;
    cout << has_const_reference_op<sub_int_ref,int &>::value  << endl;
    cout << "Expect No" << endl;
    cout << has_const_reference_op<int *,int &>::value;
    cout << has_const_reference_op<unique_ptr<int>,char &>::value;
    cout << has_const_reference_op<unique_ptr<int>,int const &>::value;
    cout << has_const_reference_op<unique_ptr<int>,int>::value;
    cout << has_const_reference_op<unique_ptr<long>,int &>::value;
    cout << has_const_reference_op<int,int>::value;
    cout << has_const_reference_op<std::vector<int>,int &>::value;
    cout << has_const_reference_op<ee_ref<int>,int &>::value;
    cout << has_const_reference_op<sub_ee_ref,int &>::value;
    cout << has_const_reference_op<empty,int &>::value  << endl;
    return 0;
}

এই ধারণায় নতুন ত্রুটি আছে কি? এটিকে এড়িয়ে যাওয়া ছদ্মবেশটিকে আরও একবার ঘৃণা না করে কী আরও জেনেরিক করা যায়?


16

এখানে কিছু ব্যবহার স্নিপেটস রয়েছে: * এই সমস্তগুলির জন্য সাহস আরও দূরে

xপ্রদত্ত শ্রেণিতে সদস্যের জন্য চেক করুন । ভ্যার, ফানক, ক্লাস, ইউনিয়ন বা এনাম হতে পারে:

CREATE_MEMBER_CHECK(x);
bool has_x = has_member_x<class_to_check_for_x>::value;

সদস্য ফাংশন জন্য পরীক্ষা করুন void x():

//Func signature MUST have T as template variable here... simpler this way :\
CREATE_MEMBER_FUNC_SIG_CHECK(x, void (T::*)(), void__x);
bool has_func_sig_void__x = has_member_func_void__x<class_to_check_for_x>::value;

সদস্য ভেরিয়েবলের জন্য পরীক্ষা করুন x:

CREATE_MEMBER_VAR_CHECK(x);
bool has_var_x = has_member_var_x<class_to_check_for_x>::value;

সদস্য শ্রেণীর জন্য পরীক্ষা করুন x:

CREATE_MEMBER_CLASS_CHECK(x);
bool has_class_x = has_member_class_x<class_to_check_for_x>::value;

সদস্য ইউনিয়নের জন্য পরীক্ষা করুন x:

CREATE_MEMBER_UNION_CHECK(x);
bool has_union_x = has_member_union_x<class_to_check_for_x>::value;

সদস্য এনামের জন্য পরীক্ষা করুন x:

CREATE_MEMBER_ENUM_CHECK(x);
bool has_enum_x = has_member_enum_x<class_to_check_for_x>::value;

xস্বাক্ষর নির্বিশেষে যে কোনও সদস্যের কার্যকারিতা পরীক্ষা করুন :

CREATE_MEMBER_CHECK(x);
CREATE_MEMBER_VAR_CHECK(x);
CREATE_MEMBER_CLASS_CHECK(x);
CREATE_MEMBER_UNION_CHECK(x);
CREATE_MEMBER_ENUM_CHECK(x);
CREATE_MEMBER_FUNC_CHECK(x);
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;

অথবা

CREATE_MEMBER_CHECKS(x);  //Just stamps out the same macro calls as above.
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;

বিশদ এবং মূল:

/*
    - Multiple inheritance forces ambiguity of member names.
    - SFINAE is used to make aliases to member names.
    - Expression SFINAE is used in just one generic has_member that can accept
      any alias we pass it.
*/

//Variadic to force ambiguity of class members.  C++11 and up.
template <typename... Args> struct ambiguate : public Args... {};

//Non-variadic version of the line above.
//template <typename A, typename B> struct ambiguate : public A, public B {};

template<typename A, typename = void>
struct got_type : std::false_type {};

template<typename A>
struct got_type<A> : std::true_type {
    typedef A type;
};

template<typename T, T>
struct sig_check : std::true_type {};

template<typename Alias, typename AmbiguitySeed>
struct has_member {
    template<typename C> static char ((&f(decltype(&C::value))))[1];
    template<typename C> static char ((&f(...)))[2];

    //Make sure the member name is consistently spelled the same.
    static_assert(
        (sizeof(f<AmbiguitySeed>(0)) == 1)
        , "Member name specified in AmbiguitySeed is different from member name specified in Alias, or wrong Alias/AmbiguitySeed has been specified."
    );

    static bool const value = sizeof(f<Alias>(0)) == 2;
};

ম্যাক্রোস (এল ডায়াবলো!):

CREATE_MEMBER_CHECK:

//Check for any member with given name, whether var, func, class, union, enum.
#define CREATE_MEMBER_CHECK(member)                                         \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct Alias_##member;                                                      \
                                                                            \
template<typename T>                                                        \
struct Alias_##member <                                                     \
    T, std::integral_constant<bool, got_type<decltype(&T::member)>::value>  \
> { static const decltype(&T::member) value; };                             \
                                                                            \
struct AmbiguitySeed_##member { char member; };                             \
                                                                            \
template<typename T>                                                        \
struct has_member_##member {                                                \
    static const bool value                                                 \
        = has_member<                                                       \
            Alias_##member<ambiguate<T, AmbiguitySeed_##member>>            \
            , Alias_##member<AmbiguitySeed_##member>                        \
        >::value                                                            \
    ;                                                                       \
}

CREATE_MEMBER_VAR_CHECK:

//Check for member variable with given name.
#define CREATE_MEMBER_VAR_CHECK(var_name)                                   \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct has_member_var_##var_name : std::false_type {};                      \
                                                                            \
template<typename T>                                                        \
struct has_member_var_##var_name<                                           \
    T                                                                       \
    , std::integral_constant<                                               \
        bool                                                                \
        , !std::is_member_function_pointer<decltype(&T::var_name)>::value   \
    >                                                                       \
> : std::true_type {}

CREATE_MEMBER_FUNC_SIG_CHECK:

//Check for member function with given name AND signature.
#define CREATE_MEMBER_FUNC_SIG_CHECK(func_name, func_sig, templ_postfix)    \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct has_member_func_##templ_postfix : std::false_type {};                \
                                                                            \
template<typename T>                                                        \
struct has_member_func_##templ_postfix<                                     \
    T, std::integral_constant<                                              \
        bool                                                                \
        , sig_check<func_sig, &T::func_name>::value                         \
    >                                                                       \
> : std::true_type {}

CREATE_MEMBER_CLASS_CHECK:

//Check for member class with given name.
#define CREATE_MEMBER_CLASS_CHECK(class_name)               \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_class_##class_name : std::false_type {};  \
                                                            \
template<typename T>                                        \
struct has_member_class_##class_name<                       \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_class<                                    \
            typename got_type<typename T::class_name>::type \
        >::value                                            \
    >                                                       \
> : std::true_type {}

CREATE_MEMBER_UNION_CHECK:

//Check for member union with given name.
#define CREATE_MEMBER_UNION_CHECK(union_name)               \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_union_##union_name : std::false_type {};  \
                                                            \
template<typename T>                                        \
struct has_member_union_##union_name<                       \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_union<                                    \
            typename got_type<typename T::union_name>::type \
        >::value                                            \
    >                                                       \
> : std::true_type {}

CREATE_MEMBER_ENUM_CHECK:

//Check for member enum with given name.
#define CREATE_MEMBER_ENUM_CHECK(enum_name)                 \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_enum_##enum_name : std::false_type {};    \
                                                            \
template<typename T>                                        \
struct has_member_enum_##enum_name<                         \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_enum<                                     \
            typename got_type<typename T::enum_name>::type  \
        >::value                                            \
    >                                                       \
> : std::true_type {}

CREATE_MEMBER_FUNC_CHECK:

//Check for function with given name, any signature.
#define CREATE_MEMBER_FUNC_CHECK(func)          \
template<typename T>                            \
struct has_member_func_##func {                 \
    static const bool value                     \
        = has_member_##func<T>::value           \
        && !has_member_var_##func<T>::value     \
        && !has_member_class_##func<T>::value   \
        && !has_member_union_##func<T>::value   \
        && !has_member_enum_##func<T>::value    \
    ;                                           \
}

CREATE_MEMBER_CHECKS:

//Create all the checks for one member.  Does NOT include func sig checks.
#define CREATE_MEMBER_CHECKS(member)    \
CREATE_MEMBER_CHECK(member);            \
CREATE_MEMBER_VAR_CHECK(member);        \
CREATE_MEMBER_CLASS_CHECK(member);      \
CREATE_MEMBER_UNION_CHECK(member);      \
CREATE_MEMBER_ENUM_CHECK(member);       \
CREATE_MEMBER_FUNC_CHECK(member)

1
এটা অসাধারণ; এটি একটি একক শিরোলেখ ফাইল লাইব্রেরিতে রাখা ভাল হবে।
অ্যালান

12

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

template <typename T, int (T::*) ()> struct enable { typedef T type; };
template <typename T> typename enable<T, &T::i>::type bla (T&);
struct A { void i(); };
struct B { int i(); };
int main()
{
  A a;
  B b;
  bla(b);
  bla(a);
}

4
thaks! এটি yrp দ্বারা প্রস্তাবিত সমাধানের মতো। আমি জানতাম না যে টেমপ্লেটটি সদস্যের কার্যক্রমে ছড়িয়ে দেওয়া যায়। আমি আজ শিখেছি এটি একটি নতুন বৈশিষ্ট্য! ... এবং একটি নতুন পাঠ: "আপনি কখনই সি ++ তে বিশেষজ্ঞ নন" :)
নন ugasoft

7

মাইক কিংহানের উত্তরটি এখানে দেওয়া সহজ। এটি উত্তরাধিকার সূত্রে প্রাপ্ত পদ্ধতিগুলি সনাক্ত করবে। এটি সঠিক স্বাক্ষরের জন্যও পরীক্ষা করবে (জ্রোকের পদ্ধতির বিপরীতে যা আর্গুমেন্ট রূপান্তরকে অনুমতি দেয়)।

template <class C>
class HasGreetMethod
{
    template <class T>
    static std::true_type testSignature(void (T::*)(const char*) const);

    template <class T>
    static decltype(testSignature(&T::greet)) test(std::nullptr_t);

    template <class T>
    static std::false_type test(...);

public:
    using type = decltype(test<C>(nullptr));
    static const bool value = type::value;
};

struct A { void greet(const char* name) const; };
struct Derived : A { };
static_assert(HasGreetMethod<Derived>::value, "");

চলমান উদাহরণ


এটি ভাল, তবে যদি ফাংশনটি কোনও যুক্তি না নেয় তবে এটি কাজ করবে না
ত্রিস্কেল্ডিয়ান 10'16

এটা দুর্দান্ত কাজ করে। কোনও যুক্তি না নিয়ে সদস্য ফাংশনে এই কৌশলটি প্রয়োগ করতে আমার কোনও সমস্যা হয়নি।
জনবি

এটি আমার জন্য একাধিক এবং কোনও পদ্ধতির যুক্তি, ওভারলোড সহ এবং উত্তরাধিকার সহ usingএবং বেস ক্লাস থেকে ওভারলোড আনতে ব্যবহার সহ ভাল কাজ করে । এটি আমার জন্য এমএসভিসি ২০১৫ এবং ক্ল্যাং-সিএল নিয়ে কাজ করে। এটি এমএসভিসি 2012 এর সাথে কাজ করে না।
স্টিভায়ার

5

আপনি std :: is_member_function_pointer ব্যবহার করতে পারেন

class A {
   public:
     void foo() {};
}

 bool test = std::is_member_function_pointer<decltype(&A::foo)>::value;

16
কিছু না থাকলে &A::fooএকটি সংকলন ত্রুটি fooহবে না A? আমি মূল প্রশ্নটি পড়েছি যেহেতু কোনও ইনপুট ক্লাসের সাথে কাজ করার কথা, ঠিক তেমন নয় যাঁর একরকম সদস্যের নাম রয়েছে foo
জেফ ওয়াল্ডেন

5

একই ধরণের সমস্যাটি আমি নিজেই নিয়ে এসেছিলাম এবং প্রস্তাবিত সমাধানগুলি এখানে খুব আকর্ষণীয় পেয়েছি ... তবে এর সমাধানের প্রয়োজনীয়তা ছিল যা:

  1. উত্তরাধিকার সূত্রে প্রাপ্ত কার্যগুলিও সনাক্ত করে;
  2. নন সি ++ 11 প্রস্তুত সংকলকগুলির সাথে সামঞ্জস্যপূর্ণ (সুতরাং কোনও ডিক্লাইপ টাইপ নয়)

একটি ভাল আলোচনার ভিত্তিতে এই জাতীয় প্রস্তাব দেওয়ার জন্য আর একটি থ্রেড পেয়েছেউন্নত :: has_ ​​* শ্রেণীর মডেল অনুসরণ করে, বৈশিষ্ট্য শ্রেণীর জন্য দুটি ম্যাক্রো ঘোষণা হিসাবে প্রস্তাবিত সমাধানটির সাধারণীকরণ এখানে দেওয়া হয়েছে ।

#include <boost/type_traits/is_class.hpp>
#include <boost/mpl/vector.hpp>

/// Has constant function
/** \param func_ret_type Function return type
    \param func_name Function name
    \param ... Variadic arguments are for the function parameters
*/
#define DECLARE_TRAITS_HAS_FUNC_C(func_ret_type, func_name, ...) \
    __DECLARE_TRAITS_HAS_FUNC(1, func_ret_type, func_name, ##__VA_ARGS__)

/// Has non-const function
/** \param func_ret_type Function return type
    \param func_name Function name
    \param ... Variadic arguments are for the function parameters
*/
#define DECLARE_TRAITS_HAS_FUNC(func_ret_type, func_name, ...) \
    __DECLARE_TRAITS_HAS_FUNC(0, func_ret_type, func_name, ##__VA_ARGS__)

// Traits content
#define __DECLARE_TRAITS_HAS_FUNC(func_const, func_ret_type, func_name, ...)  \
    template                                                                  \
    <   typename Type,                                                        \
        bool is_class = boost::is_class<Type>::value                          \
    >                                                                         \
    class has_func_ ## func_name;                                             \
    template<typename Type>                                                   \
    class has_func_ ## func_name<Type,false>                                  \
    {public:                                                                  \
        BOOST_STATIC_CONSTANT( bool, value = false );                         \
        typedef boost::false_type type;                                       \
    };                                                                        \
    template<typename Type>                                                   \
    class has_func_ ## func_name<Type,true>                                   \
    {   struct yes { char _foo; };                                            \
        struct no { yes _foo[2]; };                                           \
        struct Fallback                                                       \
        {   func_ret_type func_name( __VA_ARGS__ )                            \
                UTILITY_OPTIONAL(func_const,const) {}                         \
        };                                                                    \
        struct Derived : public Type, public Fallback {};                     \
        template <typename T, T t>  class Helper{};                           \
        template <typename U>                                                 \
        static no deduce(U*, Helper                                           \
            <   func_ret_type (Fallback::*)( __VA_ARGS__ )                    \
                    UTILITY_OPTIONAL(func_const,const),                       \
                &U::func_name                                                 \
            >* = 0                                                            \
        );                                                                    \
        static yes deduce(...);                                               \
    public:                                                                   \
        BOOST_STATIC_CONSTANT(                                                \
            bool,                                                             \
            value = sizeof(yes)                                               \
                == sizeof( deduce( static_cast<Derived*>(0) ) )               \
        );                                                                    \
        typedef ::boost::integral_constant<bool,value> type;                  \
        BOOST_STATIC_CONSTANT(bool, is_const = func_const);                   \
        typedef func_ret_type return_type;                                    \
        typedef ::boost::mpl::vector< __VA_ARGS__ > args_type;                \
    }

// Utility functions
#define UTILITY_OPTIONAL(condition, ...) UTILITY_INDIRECT_CALL( __UTILITY_OPTIONAL_ ## condition , ##__VA_ARGS__ )
#define UTILITY_INDIRECT_CALL(macro, ...) macro ( __VA_ARGS__ )
#define __UTILITY_OPTIONAL_0(...)
#define __UTILITY_OPTIONAL_1(...) __VA_ARGS__

এই ম্যাক্রোগুলি নিম্নলিখিত প্রোটোটাইপ সহ একটি বৈশিষ্ট্য শ্রেণিতে প্রসারিত:

template<class T>
class has_func_[func_name]
{
public:
    /// Function definition result value
    /** Tells if the tested function is defined for type T or not.
    */
    static const bool value = true | false;

    /// Function definition result type
    /** Type representing the value attribute usable in
        http://www.boost.org/doc/libs/1_53_0/libs/utility/enable_if.html
    */
    typedef boost::integral_constant<bool,value> type;

    /// Tested function constness indicator
    /** Indicates if the tested function is const or not.
        This value is not deduced, it is forced depending
        on the user call to one of the traits generators.
    */
    static const bool is_const = true | false;

    /// Tested function return type
    /** Indicates the return type of the tested function.
        This value is not deduced, it is forced depending
        on the user's arguments to the traits generators.
    */
    typedef func_ret_type return_type;

    /// Tested function arguments types
    /** Indicates the arguments types of the tested function.
        This value is not deduced, it is forced depending
        on the user's arguments to the traits generators.
    */
    typedef ::boost::mpl::vector< __VA_ARGS__ > args_type;
};

সুতরাং এর সাধারণ ব্যবহারটি কোনটি করতে পারে?

// We enclose the traits class into
// a namespace to avoid collisions
namespace ns_0 {
    // Next line will declare the traits class
    // to detect the member function void foo(int,int) const
    DECLARE_TRAITS_HAS_FUNC_C(void, foo, int, int);
}

// we can use BOOST to help in using the traits
#include <boost/utility/enable_if.hpp>

// Here is a function that is active for types
// declaring the good member function
template<typename T> inline
typename boost::enable_if< ns_0::has_func_foo<T> >::type
foo_bar(const T &_this_, int a=0, int b=1)
{   _this_.foo(a,b);
}

// Here is a function that is active for types
// NOT declaring the good member function
template<typename T> inline
typename boost::disable_if< ns_0::has_func_foo<T> >::type
foo_bar(const T &_this_, int a=0, int b=1)
{   default_foo(_this_,a,b);
}

// Let us declare test types
struct empty
{
};
struct direct_foo
{
    void foo(int,int);
};
struct direct_const_foo
{
    void foo(int,int) const;
};
struct inherited_const_foo :
    public direct_const_foo
{
};

// Now anywhere in your code you can seamlessly use
// the foo_bar function on any object:
void test()
{
    int a;
    foo_bar(a); // calls default_foo

    empty b;
    foo_bar(b); // calls default_foo

    direct_foo c;
    foo_bar(c); // calls default_foo (member function is not const)

    direct_const_foo d;
    foo_bar(d); // calls d.foo (member function is const)

    inherited_const_foo e;
    foo_bar(e); // calls e.foo (inherited member function)
}

5

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

  1. পদ্ধতিটি উপলব্ধ কিনা তা অনুযায়ী ভিন্ন ভিন্ন ফেরতের প্রকারের সাথে ফাংশন টেম্পলেট ওভারলোডিং
  2. type_traitsশিরোনামে মেটা শর্তাদি রেখে আমরা একটি বা ফিরে যেতে চাইtrue_typefalse_type আমাদের ওভারলোডগুলি থেকে
  3. ঘোষণা true_typeএকটি আশা জমিদার intএবং false_type: জমিদার Variadic পরামিতি আশা শোষণ "জমিদার রেজোলিউশনের ঊহ্য শব্দ রূপান্তর সর্বনিম্ন অগ্রাধিকার"
  4. true_typeফাংশনটির জন্য টেমপ্লেট স্পেসিফিকেশন সংজ্ঞায়িত করার সময় আমরা ব্যবহার করব declvalএবং decltypeপদ্ধতিগুলির মধ্যে ফিরতি প্রকারের পার্থক্য বা ওভারলোডগুলি থেকে আলাদা করে ফাংশনটি সনাক্ত করতে দেব allowing

আপনি এই একটি লাইভ উদাহরণ দেখতে পারেন এখানেতবে আমি এটি নীচেও ব্যাখ্যা করব:

আমি নামের একটি ক্রিয়াকলাপের অস্তিত্ব যাচাই করতে চাই testযা থেকে রূপান্তরযোগ্য এক ধরণের লাগে int, তারপরে আমার এই দুটি ফাংশনটি ঘোষণা করতে হবে:

template <typename T, typename S = decltype(declval<T>().test(declval<int>))> static true_type hasTest(int);
template <typename T> static false_type hasTest(...);
  • decltype(hasTest<a>(0))::valueহয় true(নোট সেখানে মোকাবেলা করার বিশেষ কার্যকারিতা তৈরি করতে কোন প্রয়োজন নেই void a::test()জমিদার, void a::test(int)গৃহীত)
  • decltype(hasTest<b>(0))::valueহয় true(কারণ intরূপান্তরযোগ্য double int b::test(double)হিসাবে গ্রহণযোগ্য, ফেরতের প্রকারের চেয়ে পৃথক)
  • decltype(hasTest<c>(0))::valueহ'ল false( cনামকরণের কোনও পদ্ধতি নেই যা এটির testথেকে রূপান্তরযোগ্য কোনও ধরণের গ্রহণ করে intকারণ এটি গৃহীত নয়)

এই দ্রষ্টব্যটির 2 টি ত্রুটি রয়েছে:

  1. এক পদ্ধতির এক জোড়া কার্যের ঘোষণা প্রয়োজন
  2. বিশেষত আমরা যদি একই নামের জন্য পরীক্ষা করতে চাই তবে নেমস্পেস দূষণ তৈরি করে, উদাহরণস্বরূপ আমরা কোন ফাংশনটির নামকরণ করব যা একটির জন্য পরীক্ষা করতে চেয়েছিল test() পদ্ধতির ?

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

#define FOO(FUNCTION, DEFINE) template <typename T, typename S = decltype(declval<T>().FUNCTION)> static true_type __ ## DEFINE(int); \
                              template <typename T> static false_type __ ## DEFINE(...); \
                              template <typename T> using DEFINE = decltype(__ ## DEFINE<T>(0));

আপনি এটি ব্যবহার করতে পারেন:

namespace details {
    FOO(test(declval<int>()), test_int)
    FOO(test(), test_void)
}

পরবর্তীকালে কল করা details::test_int<a>::valueবা details::test_void<a>::valueফলন হবে trueবা falseইনলাইন কোড বা মেটা-প্রোগ্রামিংয়ের উদ্দেশ্যে।


3

অ-অনুপ্রবেশকারী হতে, আপনি শ্রেণীবদ্ধ serializeহওয়া শ্রেণীর নাম স্থান বা আর্কাইভ ক্লাসের কোনেইগ লুকআপকে ধন্যবাদ দিতে পারেন । দেখুন জন্য বিনামূল্যে ফাংশন অগ্রাহ্য করা নামস্থান আরো বিস্তারিত জানার জন্য। :-)

একটি নিখরচায় কার্যকারিতা বাস্তবায়নের জন্য প্রদত্ত যে কোনও নেমস্পেস খোলার অর্থ হ'ল সহজ W (উদাহরণস্বরূপ, আপনার নিজের প্রকারের জন্য stdপ্রয়োগ করার swapজন্য আপনার নেমস্পেস খোলার কথা নয় তবে পরিবর্তে কোনিগ লুকআপ ব্যবহার করা উচিত))


3

আপনি সনাক্তকারী আইডিয়ম চান বলে মনে হয়। উপরের উত্তরগুলি সি ++ 11 বা সি ++ 14 এর সাথে কাজ করে এমন বিভিন্নতা।

std::experimentalগ্রন্থাগার বৈশিষ্ট্য রয়েছে যা মূলত এই কাজ হয়েছে। উপর থেকে একটি উদাহরণ কাজ করে, এটি হতে পারে:

#include <experimental/type_traits>

// serialized_method_t is a detector type for T.serialize(int) const
template<typename T>
using serialized_method_t = decltype(std::declval<const T&>.serialize(std::declval<int>()));

// has_serialize_t is std::true_type when T.serialize(int) exists,
// and false otherwise.
template<typename T>
using has_serialize_t = std::experimental::is_detected_t<serialized_method_t, T>;

আপনি যদি std :: পরীক্ষামূলক ব্যবহার করতে না পারেন তবে একটি প্রাথমিক সংস্করণটি এর মতো তৈরি করা যেতে পারে:

template <typename... Ts>
using void_t = void;
template <template <class...> class Trait, class AlwaysVoid, class... Args>
struct detector : std::false_type {};
template <template <class...> class Trait, class... Args>
struct detector<Trait, void_t<Trait<Args...>>, Args...> : std::true_type {};

// serialized_method_t is a detector type for T.serialize(int) const
template<typename T>
using serialized_method_t = decltype(std::declval<const T&>.serialize(std::declval<int>()));

// has_serialize_t is std::true_type when T.serialize(int) exists,
// and false otherwise.
template <typename T>
using has_serialize_t = typename detector<serialized_method_t, void, T>::type;

যেহেতু has_serialize_t সত্যিই হয় std :: true_type বা std :: false_type, তাই এটি সাধারণ SFINAE আইডিয়ামগুলির মাধ্যমে ব্যবহার করা যেতে পারে:

template<class T>
std::enable_if_t<has_serialize_t<T>::value, std::string>
SerializeToString(const T& t) {
}

বা ওভারলোড রেজোলিউশন সহ প্রেরণ ব্যবহার করে:

template<class T>
std::string SerializeImpl(std::true_type, const T& t) {
  // call serialize here.
}

template<class T>
std::string SerializeImpl(std::false_type, const T& t) {
  // do something else here.
}

template<class T>
std::string Serialize(const T& t) {
  return SerializeImpl(has_serialize_t<T>{}, t);
}

2

ঠিক আছে. দ্বিতীয় চেষ্টা. আপনি যদি এটিকেও পছন্দ না করেন তবে ঠিক আছে, আমি আরও আইডিয়া খুঁজছি।

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

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


হ ... আমি এই সমাধানটি বিশ্লেষণ করেছি ... আমি মনে করি এটি আমার কাঠামোর ব্যবহারকারীদের জন্য কিছুটা ব্যয়বহুল। (ঠিক আছে, আমি স্বীকার করি, আমি একটি স্ট্রিমিং ফ্রেমওয়ার্ক বিকাশ করছি এবং আমি
আইস্ট্রিম

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

1

সি ++ 11 সমর্থন ( decltype) ছাড়াই এটি কাজ করতে পারে:

SSCCE

#include <iostream>
using namespace std;

struct A { void foo(void); };
struct Aa: public A { };
struct B { };

struct retA { int foo(void); };
struct argA { void foo(double); };
struct constA { void foo(void) const; };
struct varA { int foo; };

template<typename T>
struct FooFinder {
    typedef char true_type[1];
    typedef char false_type[2];

    template<int>
    struct TypeSink;

    template<class U>
    static true_type &match(U);

    template<class U>
    static true_type &test(TypeSink<sizeof( matchType<void (U::*)(void)>( &U::foo ) )> *);

    template<class U>
    static false_type &test(...);

    enum { value = (sizeof(test<T>(0, 0)) == sizeof(true_type)) };
};

int main() {
    cout << FooFinder<A>::value << endl;
    cout << FooFinder<Aa>::value << endl;
    cout << FooFinder<B>::value << endl;

    cout << FooFinder<retA>::value << endl;
    cout << FooFinder<argA>::value << endl;
    cout << FooFinder<constA>::value << endl;
    cout << FooFinder<varA>::value << endl;
}

এটি কীভাবে আশাবাদী কাজ করে

A, Aaএবং Bপ্রশ্নগুলি মধ্যে clases হয়,Aa কী বিশেষ যেটি আমরা যে সদস্যটির সন্ধান করছি তার উত্তরাধিকার সূত্রে প্রাপ্ত।

ইন এবং সংবাদদাতা সি ++ 11 শ্রেণীর জন্য প্রতিস্থাপন হয়। এছাড়াও টেমপ্লেট মেটা প্রোগ্রামিংয়ের বোঝার জন্য, তারা SFINAE-আকারের কৌশলটির খুব ভিত্তি প্রকাশ করে।FooFindertrue_typefalse_type

TypeSinkএকটি টেমপ্লেট struct যে পরে ব্যবহার করা হয় এর অবিচ্ছেদ্য ফলাফলের ডুবা হয় sizeofএকটি টেমপ্লেট ইনস্ট্যান্স মধ্যে অপারেটর একটি টাইপ গঠন।

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

testএনাম ঘোষণার সাথে উভয় ফাংশন অবশেষে কেন্দ্রীয় SFINAE প্যাটার্ন গঠন করে। উপবৃত্তাকার ব্যবহার করে এমন একটি জেনেরিক রয়েছে যা ফিরে আসেfalse_type আরও নির্দিষ্ট যুক্তিযুক্ত অগ্রাধিকার গ্রহণের জন্য এবং একটি পাল্টা ।

এর testএকটি টেমপ্লেট আর্গুমেন্ট দিয়ে ফাংশনটি ইনস্ট্যান্ট করতে সক্ষম হওয়ার জন্য T, matchফাংশনটি তাত্ক্ষণিকভাবে আবশ্যক, কারণ তার রিটার্ন টাইপটি TypeSinkযুক্তিটি তাত্ক্ষণিকভাবে আবশ্যক করা প্রয়োজন । সতর্কবাণীটি হ'ল &U::foo, একটি ফাংশন আর্গুমেন্টে আবৃত হওয়া, কোনও টেম্পলেট আর্গুমেন্ট বিশেষজ্ঞের মধ্যে থেকে উল্লেখ করা হয়নি , সুতরাং উত্তরাধিকার সূত্রে সদস্য অনুসন্ধান এখনও ঘটে member


1

আপনি যদি ফেসবুক ফালতু ব্যবহার করেন তবে এগুলি আপনাকে সহায়তা করার জন্য বক্স ম্যাক্রোর বাইরে চলে গেছে:

#include <folly/Traits.h>
namespace {
  FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(has_test_traits, test);
} // unnamed-namespace

void some_func() {
  cout << "Does class Foo have a member int test() const? "
    << boolalpha << has_test_traits<Foo, int() const>::value;
}

যদিও পূর্বের উত্তরের সাথে প্রয়োগের বিবরণগুলি একই, তবে একটি লাইব্রেরি ব্যবহার সহজ is


0

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

  • স্থির সদস্য ফাংশন
  • অ স্থির সদস্য ফাংশন
  • অ স্থিতিশীল সদস্য ফাংশন কনস্ট

একটি নির্দিষ্ট স্বাক্ষর সহ। যেহেতু আমার কোনও স্বাক্ষর ক্যাপচার করার দরকার নেই (এর জন্য আরও জটিল সমাধানের প্রয়োজন হবে), এটি আমার কাছে স্যুট। এটি মূলত সক্ষম_আইফ_টি ব্যবহার করে

struct Foo{ static int sum(int, const double&){return 0;} };
struct Bar{ int calc(int, const double&) {return 1;} };
struct BarConst{ int calc(int, const double&) const {return 1;} };

// Note : second typename can be void or anything, as long as it is consistent with the result of enable_if_t
template<typename T, typename = T> struct has_static_sum : std::false_type {};
template<typename T>
struct has_static_sum<typename T,
                        std::enable_if_t<std::is_same<decltype(T::sum), int(int, const double&)>::value,T> 
                      > : std::true_type {};

template<typename T, typename = T> struct has_calc : std::false_type {};
template<typename T>
struct has_calc <typename T,
                  std::enable_if_t<std::is_same<decltype(&T::calc), int(T::*)(int, const double&)>::value,T>
                > : std::true_type {};

template<typename T, typename = T> struct has_calc_const : std::false_type {};
template<typename T>
struct has_calc_const <typename T,
                        std::enable_if_t<std::is_same<decltype(&T::calc), int(T::*)(int, const double&) const>::value,T>
                      > : std::true_type {};

int main ()
{
    constexpr bool has_sum_val = has_static_sum<Foo>::value;
    constexpr bool not_has_sum_val = !has_static_sum<Bar>::value;

    constexpr bool has_calc_val = has_calc<Bar>::value;
    constexpr bool not_has_calc_val = !has_calc<Foo>::value;

    constexpr bool has_calc_const_val = has_calc_const<BarConst>::value;
    constexpr bool not_has_calc_const_val = !has_calc_const<Bar>::value;

    std::cout<< "           has_sum_val " << has_sum_val            << std::endl
             << "       not_has_sum_val " << not_has_sum_val        << std::endl
             << "          has_calc_val " << has_calc_val           << std::endl
             << "      not_has_calc_val " << not_has_calc_val       << std::endl
             << "    has_calc_const_val " << has_calc_const_val     << std::endl
             << "not_has_calc_const_val " << not_has_calc_const_val << std::endl;
}

আউটপুট:

           has_sum_val 1
       not_has_sum_val 1
          has_calc_val 1
      not_has_calc_val 1
    has_calc_const_val 1
not_has_calc_const_val 1

0

উপর নির্মাণের jrok এর উত্তর , আমি নেস্টেড টেমপ্লেট শ্রেণীর এবং / অথবা ফাংশন ব্যবহার করে এড়িয়ে যাওয়া হয়েছে।

#include <type_traits>

#define CHECK_NESTED_FUNC(fName) \
    template <typename, typename, typename = std::void_t<>> \
    struct _has_##fName \
    : public std::false_type {}; \
    \
    template <typename Class, typename Ret, typename... Args> \
    struct _has_##fName<Class, Ret(Args...), \
        std::void_t<decltype(std::declval<Class>().fName(std::declval<Args>()...))>> \
    : public std::is_same<decltype(std::declval<Class>().fName(std::declval<Args>()...)), Ret> \
    {}; \
    \
    template <typename Class, typename Signature> \
    using has_##fName = _has_##fName<Class, Signature>;

#define HAS_NESTED_FUNC(Class, Func, Signature) has_##Func<Class, Signature>::value

উপরের ম্যাক্রোগুলি আমরা নীচের মতো ব্যবহার করতে পারি:

class Foo
{
public:
    void Bar(int, const char *) {}
};

CHECK_NESTED_FUNC(Bar);  // generate required metafunctions

int main()
{
    using namespace std;
    cout << boolalpha
         << HAS_NESTED_FUNC(Foo, Bar, void(int, const char *))  // prints true
         << endl;
    return 0;
}

পরামর্শ স্বাগত জানাই।

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