c++ - অবজ - সি++ বই




একটি ফাংশন এর অস্তিত্ব পরীক্ষা করার জন্য একটি টেমপ্লেট লিখতে কি সম্ভব? (17)

সনাক্তকরণ টুলকিট

N4502 সি ++ 17 স্ট্যান্ডার্ড লাইব্রেরির অন্তর্ভুক্তির জন্য একটি সনাক্তকরণ N4502 প্রস্তাব করে যা কিছুটা মার্জিতভাবে সমস্যার সমাধান করতে পারে। তাছাড়া, এটি কেবল লাইব্রেরি মৌলিক বিষয়গুলি TS v2 তে গৃহীত হয়েছে। এটি std::is_detected সহ কিছু std::is_detected যা এটি সহজে লিখতে টাইপ বা ফাংশন সনাক্তকরণ metafunctions লিখতে ব্যবহার করা যেতে পারে। এখানে আপনি কীভাবে এটি ব্যবহার করতে পারেন:

template<typename T>
using toString_t = decltype( std::declval<T&>().toString() );

template<typename T>
constexpr bool has_toString = std::is_detected_v<toString_t, T>;

উল্লেখ্য যে উপরে উল্লিখিত উদাহরণ নেই। সনাক্তকরণ টুলকিটটি এখনও আদর্শ লাইব্রেরিগুলিতে উপলব্ধ নয় তবে প্রস্তাবটি সম্পূর্ণ বাস্তবায়ন রয়েছে যা আপনি যদি সত্যিই এটির প্রয়োজনে সহজেই অনুলিপি করতে পারেন। if constexpr + 17 বৈশিষ্ট্যটি if constexpr :

template<class T>
std::string optionalToString(T* obj)
{
    if constexpr (has_toString<T>)
        return obj->toString();
    else
        return "toString not defined";
}

Boost.TTI

আরও কিছুটা ছদ্মবেশী টুলকিট যেমন একটি পরীক্ষা সঞ্চালন করতে - এমনকি কম মার্জিত - Boost.TTI , বুস্ট 1.54.0 এ উপস্থাপিত। আপনার উদাহরণের জন্য, আপনাকে ম্যাক্রো BOOST_TTI_HAS_MEMBER_FUNCTION ব্যবহার করতে হবে। এখানে আপনি কীভাবে এটি ব্যবহার করতে পারেন:

#include <boost/tti/has_member_function.hpp>

// Generate the metafunction
BOOST_TTI_HAS_MEMBER_FUNCTION(toString)

// Check whether T has a member function toString
// which takes no parameter and returns a std::string
constexpr bool foo = has_member_function_toString<T, std::string>::value;

তারপর, আপনি একটি SFINAE চেক তৈরি করতে bool ব্যবহার করতে পারে।

ব্যাখ্যা

ম্যাক্রো BOOST_TTI_HAS_MEMBER_FUNCTION তৈরি has_member_function_toString যা চেক has_member_function_toString তার প্রথম টেম্পলেট প্যারামিটার হিসাবে নেয়। দ্বিতীয় টেমপ্লেট প্যারামিটার সদস্য ফাংশনের রিটার্ন টাইপের সাথে সংশ্লিষ্ট, এবং নিম্নলিখিত পরামিতি ফাংশনের প্যারামিটারগুলির সাথে সম্পর্কিত। ক্লাস T সদস্যের std::string toString() থাকলে সদস্যের value true থাকে।

বিকল্পভাবে, has_member_function_toString একটি ফাংশন পয়েন্টারকে টেমপ্লেট প্যারামিটার হিসাবে গ্রহণ করতে পারে। অতএব, has_member_function_toString<T, std::string>::value has_member_function_toString<std::string T::* ()>::value দ্বারা প্রতিস্থাপন করা সম্ভব।

একটি নির্দিষ্ট টেমপ্লেট কোন শ্রেণীতে সংজ্ঞায়িত করা হয় কিনা তার উপর নির্ভর করে আচরণ পরিবর্তন করে এমন একটি টেমপ্লেট লিখতে পারে?

আমি যা লিখতে চাই তার একটি সহজ উদাহরণ এখানে দেওয়া হল:

template<class T>
std::string optionalToString(T* obj)
{
    if (FUNCTION_EXISTS(T->toString))
        return obj->toString();
    else
        return "toString not defined";
}

সুতরাং, যদি class T এর toString() সংজ্ঞায়িত থাকে তবে এটি ব্যবহার করে; অন্যথায়, এটা না। "FUNCTION_EXISTS" অংশটি কীভাবে করতে হবে তা আমি জানি না এমন ঐন্দ্রজালিক অংশ।


C ++ এর জন্য SFINAE ব্যবহার করতে দেয় (লক্ষ্য করুন যে C ++ 11 বৈশিষ্ট্যগুলির সাথে এটি সহজতর কারণ এটি বর্ধিত SFINAE প্রায় আনুমানিক এক্সপ্রেশনগুলিতে সমর্থন করে - নীচে নিচের C ++ 03 কম্পাইলারগুলির সাথে কাজ করার জন্য তৈরি করা হয়েছে):

#define HAS_MEM_FUNC(func, name)                                        \
    template<typename T, typename Sign>                                 \
    struct name {                                                       \
        typedef char yes[1];                                            \
        typedef char no [2];                                            \
        template <typename U, U> struct type_check;                     \
        template <typename _1> static yes &chk(type_check<Sign, &_1::func > *); \
        template <typename   > static no  &chk(...);                    \
        static bool const value = sizeof(chk<T>(0)) == sizeof(yes);     \
    }

উপরে উল্লিখিত টেমপ্লেট এবং ম্যাক্রো একটি টেমপ্লেট তাত্ক্ষণিকভাবে চেষ্টা করে, এটি একটি সদস্য ফাংশন পয়েন্টার টাইপ, এবং প্রকৃত সদস্য ফাংশন পয়েন্টার প্রদান করে। যদি প্রকারগুলি উপযুক্ত না হয় তবে SFINAE টেমপ্লেটটিকে উপেক্ষা করা হতে পারে। এই মত ব্যবহার:

HAS_MEM_FUNC(toString, has_to_string);

template<typename T> void
doSomething() {
   if(has_to_string<T, std::string(T::*)()>::value) {
      ...
   } else {
      ...
   }
}

তবে মনে রাখবেন যে আপনি যদি সেই শাখা ফাংশনটি কল করতে না পারেন তবে শাখাটি। যেহেতু কম্পাইলার উভয় শাখায় বৈধতা যাচাই করবে, যে ক্ষেত্রে ফাংশন বিদ্যমান না ক্ষেত্রে ব্যর্থ হবে। এক উপায় আবার একবার SFINAE ব্যবহার করতে হয় (enable_if boost থেকেও অর্জিত হতে পারে):

template<bool C, typename T = void>
struct enable_if {
  typedef T type;
};

template<typename T>
struct enable_if<false, T> { };

HAS_MEM_FUNC(toString, has_to_string);

template<typename T> 
typename enable_if<has_to_string<T, 
                   std::string(T::*)()>::value, std::string>::type
doSomething(T * t) {
   /* something when T has toString ... */
   return t->toString();
}

template<typename T> 
typename enable_if<!has_to_string<T, 
                   std::string(T::*)()>::value, std::string>::type
doSomething(T * t) {
   /* something when T doesnt have toString ... */
   return "T::toString() does not exist.";
}

এটা ব্যবহার করে মজা আছে। এটির সুবিধা হল এটি ওভারলোড হওয়া সদস্য ফাংশনগুলির জন্য এবং কনস্ট সদস্য সদস্যের জন্যও কাজ করে ( std::string(T::*)() const ব্যবহার করে মনে রাখবেন সদস্য ফাংশন পয়েন্টার টাইপ হিসাবে!)।


এই জন্য কি ধরণের বৈশিষ্ট্য আছে। দুর্ভাগ্যবশত, তারা ম্যানুয়ালি সংজ্ঞায়িত করা আছে। আপনার ক্ষেত্রে, কল্পনা করুন:

template <typename T>
struct response_trait {
    static bool const has_tostring = false;
};

template <>
struct response_trait<your_type_with_tostring> {
    static bool const has_tostring = true;
}

এই প্রশ্নটি পুরানো, কিন্তু C ++ 11 এর সাথে আমরা ফাংশন অস্তিত্ব (অথবা কোনও অ-টাইপ সদস্যের অস্তিত্বের অস্তিত্ব) যাচাই করার জন্য একটি নতুন উপায় পেয়েছি, আবার SFINAE এ নির্ভর করে:

template<class T>
auto serialize_imp(std::ostream& os, T const& obj, int)
    -> decltype(os << obj, void())
{
  os << obj;
}

template<class T>
auto serialize_imp(std::ostream& os, T const& obj, long)
    -> decltype(obj.stream(os), void())
{
  obj.stream(os);
}

template<class T>
auto serialize(std::ostream& os, T const& obj)
    -> decltype(serialize_imp(os, obj, 0), void())
{
  serialize_imp(os, obj, 0);
}

এখন কিছু ব্যাখ্যা সম্মুখের দিকে। প্রথম জিনিস, আমি বর্ধিত রেজোলিউশন থেকে serialize(_imp) ফাংশনগুলিকে বর্ধিত করার জন্য এক্সপ্রেশন SFINAE ব্যবহার করি, যদি decltype ভিতরে প্রথম অভিব্যক্তিটি বৈধ না হয় ( decltype , ফাংশন বিদ্যমান না)।

void() সমস্ত ফাংশন ফেরত টাইপ করতে ব্যবহৃত হয়।

0 যুক্তি os << obj ওভারলোড পছন্দ করার জন্য ব্যবহৃত হয় যদি উভয় উপলব্ধ থাকে (আক্ষরিক 0 টাইপ int এবং এটির প্রথম ওভারলোড একটি ভাল মিল)।

এখন, আপনি সম্ভবত একটি বৈশিষ্ট্য একটি ফাংশন বিদ্যমান কিনা তা পরীক্ষা করতে চান। সৌভাগ্যক্রমে, এটা লিখতে সহজ। তবে, মনে রাখবেন যে, আপনি যা চান তার প্রতিটি ফাংশনের নামটির জন্য আপনাকে নিজেকে একটি বৈশিষ্ট্য লিখতে হবে।

#include <type_traits>

template<class>
struct sfinae_true : std::true_type{};

namespace detail{
  template<class T, class A0>
  static auto test_stream(int)
      -> sfinae_true<decltype(std::declval<T>().stream(std::declval<A0>()))>;
  template<class, class A0>
  static auto test_stream(long) -> std::false_type;
} // detail::

template<class T, class Arg>
struct has_stream : decltype(detail::test_stream<T, Arg>(0)){};

লাইভ উদাহরণ।

এবং ব্যাখ্যা উপর। প্রথমত, sfinae_true একটি সহায়ক ধরনের, এবং এটি মূলত লিখিত decltype(void(std::declval<T>().stream(a0)), std::true_type{}) হিসাবে একই পরিমাণ। সুবিধা সহজ যে এটি সংক্ষিপ্ত।
পরবর্তী, struct has_stream : decltype(...) std::false_type in decltype পরীক্ষা ব্যর্থ বা না কিনা তার উপর নির্ভর করে, std::true_type বা std::false_type এ থেকে উত্তরাধিকারী হয়।
সর্বশেষ, std::declval আপনাকে আপনার যে কোনও প্রকারের মানটি "মান" দেয়, আপনি কীভাবে এটি নির্মাণ করতে পারেন তা জানার প্রয়োজন ছাড়া। উল্লেখ্য যে এটি একটি আনুমানিক প্রেক্ষাপটে, যেমন decltype , decltype এবং অন্যান্যদের মধ্যেই সম্ভব।

উল্লেখ্য যে decltype অগত্যা প্রয়োজন হয় না, sizeof (এবং সমস্ত অনির্ভর প্রসঙ্গে) যে বর্ধিতকরণ পেয়েছিলাম। এটা ঠিক যে decltype ইতিমধ্যে একটি টাইপ বিতরণ এবং যেমন শুধুমাত্র ক্লিনার হয়। এখানে ওভারলোডগুলির একটি sizeof সংস্করণ রয়েছে:

template<class T>
void serialize_imp(std::ostream& os, T const& obj, int,
    int(*)[sizeof((os << obj),0)] = 0)
{
  os << obj;
}

int এবং long পরামিতি একই কারণে এখনও আছে। অ্যারে পয়েন্টার ব্যবহার করা হয় একটি প্রেক্ষাপটে যেখানে sizeof ব্যবহার করা যেতে পারে।


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

template <class Type>
class TypeHasToString
{
    // This type won't compile if the second template parameter isn't of type T,
    // so I can put a function pointer type in the first parameter and the function
    // itself in the second thus checking that the function has a specific signature.
    template <typename T, T> struct TypeCheck;

    typedef char Yes;
    typedef long No;

    // A helper struct to hold the declaration of the function pointer.
    // Change it if the function signature changes.
    template <typename T> struct ToString
    {
        typedef void (T::*fptr)();
    };

    template <typename T> static Yes HasToString(TypeCheck< typename ToString<T>::fptr, &T::toString >*);
    template <typename T> static No  HasToString(...);

public:
    static bool const value = (sizeof(HasToString<Type>(0)) == sizeof(Yes));
};

আমি gcc 4.1.2 সঙ্গে এটি চেক। ক্রেডিট প্রধানত নিকোলা বোনেলি এবং জোহানেস শাউবের কাছে যায়, তাই যদি আমার উত্তর আপনাকে সাহায্য করে তবে তাদের ভোট দিতে দিন :)


সাধারণ সমস্যাটির জন্য এটি একটি সি ++ 11 সমাধান, যদি "আমি এক্স করি, এটি কম্পাইল হবে?"

template<class> struct type_sink { typedef void type; }; // consumes a type, and makes it `void`
template<class T> using type_sink_t = typename type_sink<T>::type;
template<class T, class=void> struct has_to_string : std::false_type {}; \
template<class T> struct has_to_string<
  T,
  type_sink_t< decltype( std::declval<T>().toString() ) >
>: std::true_type {};

Trait has_to_string যেমন has_to_string<T>::value যদি true হয় এবং শুধুমাত্র যদি T এর একটি পদ্ধতি থাকে। থেকে .toString যা এই প্রসঙ্গে 0 টি আর্গুমেন্ট সহ আহ্বান করা যেতে পারে।

পরবর্তী, আমি ট্যাগ প্রেরণ ব্যবহার করবো:

namespace details {
  template<class T>
  std::string optionalToString_helper(T* obj, std::true_type /*has_to_string*/) {
    return obj->toString();
  }
  template<class T>
  std::string optionalToString_helper(T* obj, std::false_type /*has_to_string*/) {
    return "toString not defined";
  }
}
template<class T>
std::string optionalToString(T* obj) {
  return details::optionalToString_helper( obj, has_to_string<T>{} );
}

যা জটিল SFINAE এক্সপ্রেশন বেশী রক্ষণযোগ্য হতে থাকে।

যদি আপনি নিজে নিজে এটি করছেন তবে আপনি এই বৈশিষ্ট্যগুলিকে একটি ম্যাক্রো দিয়ে লিখতে পারেন তবে তারা তুলনামূলকভাবে সহজ (প্রতিটি লাইন কয়েকটি) যাতে সম্ভবত এটি মূল্যহীন না হয়:

#define MAKE_CODE_TRAIT( TRAIT_NAME, ... ) \
template<class T, class=void> struct TRAIT_NAME : std::false_type {}; \
template<class T> struct TRAIT_NAME< T, type_sink_t< decltype( __VA_ARGS__ ) > >: std::true_type {};

MAKE_CODE_TRAIT কি একটি ম্যাক্রো MAKE_CODE_TRAIT তৈরি করে। আপনি এটির বৈশিষ্ট্যের নামটি আপনার কাছে প্রেরণ করেন এবং টাইপটি পরীক্ষা করতে পারেন এমন কিছু কোড। এভাবে:

MAKE_CODE_TRAIT( has_to_string, std::declval<T>().toString() )

উপরের বৈশিষ্ট্য ক্লাস তৈরি করে।

সরাইয়া হিসাবে, উপরের প্রযুক্তিটি এমএসকে "এক্সপ্রেশন SFINAE" বলে সম্বোধন করে, এবং তাদের ২013 সংকলকটি বেশ শক্ত করে ব্যর্থ হয়।

উল্লেখ্য যে C ++ 1y এ নিম্নলিখিত সিনট্যাক্স সম্ভব:

template<class T>
std::string optionalToString(T* obj) {
  return compiled_if< has_to_string >(*obj, [&](auto&& obj) {
    return obj.toString();
  }) *compiled_else ([&]{ 
    return "toString not defined";
  });
}

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


হ্যাঁ, SFINAE দিয়ে আপনি একটি নির্দিষ্ট শ্রেণী প্রদান করে কিনা তা যাচাই করতে পারেন। এখানে কাজ কোড:

#include <iostream>

struct Hello
{
    int helloworld() { return 0; }
};

struct Generic {};    

// SFINAE test
template <typename T>
class has_helloworld
{
    typedef char one;
    typedef long two;

    template <typename C> static one test( typeof(&C::helloworld) ) ;
    template <typename C> static two test(...);    

public:
    enum { value = sizeof(test<T>(0)) == sizeof(char) };
};

int main(int argc, char *argv[])
{
    std::cout << has_helloworld<Hello>::value << std::endl;
    std::cout << has_helloworld<Generic>::value << std::endl;
    return 0;
}

আমি শুধু লিনাক্স এবং জিপিএস 4.1 / 4.3 এর সাথে এটি পরীক্ষা করেছি। আমি বিভিন্ন কম্পাইলার চলমান অন্যান্য প্ল্যাটফর্মের পোর্টেবল কিনা জানি না।


অদ্ভুত কেউ এই সাইটে খুব একবার দেখেছেন যে নিম্নলিখিত চমৎকার কৌশল:

template <class T>
struct has_foo
{
    struct S { void foo(...); };
    struct derived : S, T {};

    template <typename V, V> struct W {};

    template <typename X>
    char (&test(W<void (X::*)(), &X::foo> *))[1];

    template <typename>
    char (&test(...))[2];

    static const bool value = sizeof(test<derived>(0)) == 1;
};

আপনি নিশ্চিত যে টি টি একটি বর্গ। মনে হচ্ছে Foo সন্ধানে অস্পষ্টতা একটি প্রতিস্থাপন ব্যর্থতা। আমি এটা GCC কাজ করে, যদিও এটা মান যদিও নিশ্চিত না।


আমি অন্য একটি থ্রেডে এই উত্তরটি লিখেছিলাম যে (উপরের সমাধানগুলির বিপরীতে) উত্তরাধিকারসূত্রে সদস্যের ফাংশনগুলি চেক করে:

SFINAE উত্তরাধিকারী সদস্য ফাংশন জন্য চেক

এখানে যে সমাধান থেকে কিছু উদাহরণ:

Example1:

আমরা নিম্নলিখিত স্বাক্ষর সঙ্গে একটি সদস্য জন্য চেক করা হয়: T::const_iterator begin() const

template<class T> struct has_const_begin
{
    typedef char (&Yes)[1];
    typedef char (&No)[2];

    template<class U> 
    static Yes test(U const * data, 
                    typename std::enable_if<std::is_same<
                             typename U::const_iterator, 
                             decltype(data->begin())
                    >::value>::type * = 0);
    static No test(...);
    static const bool value = sizeof(Yes) == sizeof(has_const_begin::test((typename std::remove_reference<T>::type*)0));
};

অনুগ্রহ করে লক্ষ্য করুন যে এটি এমনকি পদ্ধতির সংশ্লেষ পরীক্ষা করে এবং আদিম প্রকারের সাথেও কাজ করে। (আমি has_const_begin<int>::valueমিথ্যা বলি এবং কম্পাইল-টাইম ত্রুটি সৃষ্টি করে না।)

উদাহরণ 2

এখন আমরা স্বাক্ষর খুঁজছেন: void foo(MyClass&, unsigned)

template<class T> struct has_foo
{
    typedef char (&Yes)[1];
    typedef char (&No)[2];

    template<class U>
    static Yes test(U * data, MyClass* arg1 = 0,
                    typename std::enable_if<std::is_void<
                             decltype(data->foo(*arg1, 1u))
                    >::value>::type * = 0);
    static No test(...);
    static const bool value = sizeof(Yes) == sizeof(has_foo::test((typename std::remove_reference<T>::type*)0));
};

দয়া করে লক্ষ্য করুন যে MyClass ডিফল্ট গঠনযোগ্য বা কোন বিশেষ ধারণা সন্তুষ্ট করতে হবে না। কৌশল পাশাপাশি টেমপ্লেট সদস্যদের সাথে কাজ করে।

আমি আগ্রহীভাবে এই বিষয়ে মতামত অপেক্ষা করছি।


এখানে কাজের কোড একটি উদাহরণ।

template<typename T>
using toStringFn = decltype(std::declval<const T>().toString());

template <class T, toStringFn<T>* = nullptr>
std::string optionalToString(const T* obj, int)
{
    return obj->toString();
}

template <class T>
std::string optionalToString(const T* obj, long)
{
    return "toString not defined";
}

int main()
{
    A* a;
    B* b;

    std::cout << optionalToString(a, 0) << std::endl; // This is A
    std::cout << optionalToString(b, 0) << std::endl; // toString not defined
}

toStringFn<T>* = nullptrফাংশন যা অতিরিক্ত লাগে সক্ষম করবে intআমার যুক্তি, যা ফাংশন যা লাগে উপর একটি অগ্রাধিকার আছে longযখন সাথে কল 0

trueফাংশন বাস্তবায়িত হলে ফাংশনগুলির জন্য আপনি একই নীতি ব্যবহার করতে পারেন ।

template <typename T>
constexpr bool toStringExists(long)
{
    return false;
}

template <typename T, toStringFn<T>* = nullptr>
constexpr bool toStringExists(int)
{
    return true;
}


int main()
{
    A* a;
    B* b;

    std::cout << toStringExists<A>(0) << std::endl; // true
    std::cout << toStringExists<B>(0) << std::endl; // false
}

কিভাবে এই সমাধান সম্পর্কে?

#include <type_traits>

template <typename U, typename = void> struct hasToString : std::false_type { };

template <typename U>
struct hasToString<U,
  typename std::enable_if<bool(sizeof(&U::toString))>::type
> : std::true_type { };

লিটব দ্বারা উপস্থাপিত স্ট্যান্ডার্ড সি ++ সমাধানটি বেস বর্গের পদ্ধতিটি যদি সংজ্ঞায়িত করা হয় তবে প্রত্যাশিত হিসাবে কাজ করবে না।

এই পরিস্থিতি পরিচালনা করে এমন একটি সমাধানের জন্য উল্লেখ করুন:

রুশ ভাষায়: http://www.rsdn.ru/forum/message/2759773.1.aspx

রোমান দ্বারা ইংরেজী অনুবাদ। পেপারলিটিস: http://groups.google.com/group/comp.lang.c++.moderated/tree/browse_frm/thread/4f7c7a96f9afbe44/c95a7b4c645e449f?pli=1

এটা অতিশয় চতুর। তবে এই সলিউশনটির সাথে একটি সমস্যা হল যে কম্পাইলারের ত্রুটি পরীক্ষা করা হয় যদি এটি বেস বর্গ হিসাবে ব্যবহার করা যায় না (উদাহরণস্বরূপ প্রাথমিক প্রকার)

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


আপনি C ++ 14 এ সমস্ত মেটাপোগ্রামিং এড়িয়ে যেতে পারেন, এবং শুধুমাত্র Fit লাইব্রেরি fit::conditionalথেকে এটি ব্যবহার করুন :Fit

template<class T>
std::string optionalToString(T* x)
{
    return fit::conditional(
        [](auto* obj) -> decltype(obj->toString()) { return obj->toString(); },
        [](auto*) { return "toString not defined"; }
    )(x);
}

এছাড়াও আপনি lambdas থেকে সরাসরি ফাংশন তৈরি করতে পারেন:

FIT_STATIC_LAMBDA_FUNCTION(optionalToString) = fit::conditional(
    [](auto* obj) -> decltype(obj->toString(), std::string()) { return obj->toString(); },
    [](auto*) -> std::string { return "toString not defined"; }
);

যাইহোক, যদি আপনি একটি কম্পাইলার ব্যবহার করেন যা জেনেরিক লেম্বডাসগুলিকে সমর্থন করে না তবে আপনাকে পৃথক ফাংশন বস্তুগুলি লিখতে হবে:

struct withToString
{
    template<class T>
    auto operator()(T* obj) const -> decltype(obj->toString(), std::string())
    {
        return obj->toString();
    }
};

struct withoutToString
{
    template<class T>
    std::string operator()(T*) const
    {
        return "toString not defined";
    }
};

FIT_STATIC_FUNCTION(optionalToString) = fit::conditional(
    withToString(),
    withoutToString()
);

আমি https://.com/a/264088/2712152 প্রদত্ত সমাধানটি একটু বেশি সাধারণ করতে সংশোধন করেছি। যেহেতু এটি নতুন সি ++ 11 বৈশিষ্ট্যগুলি ব্যবহার করে না তাই আমরা পুরানো কম্পাইলারগুলির সাথে এটি ব্যবহার করতে পারি এবং এমএসভিসি-এর সাথেও কাজ করতে হবে। কিন্তু কম্পাইলারগুলি সি 99 ব্যবহার করতে সক্ষম হওয়া উচিত কারণ এটি ভেরিয়েডিক ম্যাক্রো ব্যবহার করে।

নিচের ম্যাক্রো ব্যবহার করা যেতে পারে কিনা কোন বিশেষ শ্রেণীর নির্দিষ্ট টাইপডেফ আছে কিনা তা যাচাই করতে।

/** 
 * @class      : HAS_TYPEDEF
 * @brief      : This macro will be used to check if a class has a particular
 * typedef or not.
 * @param typedef_name : Name of Typedef
 * @param name  : Name of struct which is going to be run the test for
 * the given particular typedef specified in typedef_name
 */
#define HAS_TYPEDEF(typedef_name, name)                           \
   template <typename T>                                          \
   struct name {                                                  \
      typedef char yes[1];                                        \
      typedef char no[2];                                         \
      template <typename U>                                       \
      struct type_check;                                          \
      template <typename _1>                                      \
      static yes& chk(type_check<typename _1::typedef_name>*);    \
      template <typename>                                         \
      static no& chk(...);                                        \
      static bool const value = sizeof(chk<T>(0)) == sizeof(yes); \
   }

নির্দিষ্ট ম্যাক্রো কোন নির্দিষ্ট সদস্যের কোনো নির্দিষ্ট সংখ্যক আর্গুমেন্টের সাথে কাজ করে কিনা তা পরীক্ষা করার জন্য ব্যবহার করা যেতে পারে।

/** 
 * @class      : HAS_MEM_FUNC
 * @brief      : This macro will be used to check if a class has a particular
 * member function implemented in the public section or not. 
 * @param func : Name of Member Function
 * @param name : Name of struct which is going to be run the test for
 * the given particular member function name specified in func
 * @param return_type: Return type of the member function
 * @param ellipsis(...) : Since this is macro should provide test case for every
 * possible member function we use variadic macros to cover all possibilities
 */
#define HAS_MEM_FUNC(func, name, return_type, ...)                \
   template <typename T>                                          \
   struct name {                                                  \
      typedef return_type (T::*Sign)(__VA_ARGS__);                \
      typedef char yes[1];                                        \
      typedef char no[2];                                         \
      template <typename U, U>                                    \
      struct type_check;                                          \
      template <typename _1>                                      \
      static yes& chk(type_check<Sign, &_1::func>*);              \
      template <typename>                                         \
      static no& chk(...);                                        \
      static bool const value = sizeof(chk<T>(0)) == sizeof(yes); \
   }

আমরা has_typedef এবং has_mem_func এর জন্য চেকগুলি সম্পাদন করতে উপরের 2 ম্যাক্রোগুলি ব্যবহার করতে পারি:

class A {
public:
  typedef int check;
  void check_function() {}
};

class B {
public:
  void hello(int a, double b) {}
  void hello() {}
};

HAS_MEM_FUNC(check_function, has_check_function, void, void);
HAS_MEM_FUNC(hello, hello_check, void, int, double);
HAS_MEM_FUNC(hello, hello_void_check, void, void);
HAS_TYPEDEF(check, has_typedef_check);

int main() {
  std::cout << "Check Function A:" << has_check_function<A>::value << std::endl;
  std::cout << "Check Function B:" << has_check_function<B>::value << std::endl;
  std::cout << "Hello Function A:" << hello_check<A>::value << std::endl;
  std::cout << "Hello Function B:" << hello_check<B>::value << std::endl;
  std::cout << "Hello void Function A:" << hello_void_check<A>::value << std::endl;
  std::cout << "Hello void Function B:" << hello_void_check<B>::value << std::endl;
  std::cout << "Check Typedef A:" << has_typedef_check<A>::value << std::endl;
  std::cout << "Check Typedef B:" << has_typedef_check<B>::value << std::endl;
}

এখন এটি একটি চমৎকার সামান্য ধাঁধা ছিল - মহান প্রশ্ন!

এখানে নিকোলা বোনেলির সমাধানের বিকল্প যা অ-মানক typeofঅপারেটরের উপর নির্ভর করে না ।

দুর্ভাগ্যবশত, এটি GCC (MINGW) 3.4.5 বা ডিজিটাল মঙ্গল 8.42n তে কাজ করে না, তবে এটি MSVC এর সমস্ত সংস্করণগুলিতে (ভিসি 6 সহ) এবং কমও সি ++ এ কাজ করে।

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

আপডেট - 7 নভেম্বর 2008:

মনে হচ্ছে এটির কোডটি সংশ্লেষিকভাবে সঠিক, এমএসভিসি এবং কামাউ সি ++ শো এমন আচরণকে অনুসরণ করে না ( litbটিমার্মান এবং litb সঠিক দিক নির্দেশ করার জন্য ধন্যবাদ )। সি ++ 03 স্ট্যান্ডার্ড নিম্নলিখিত বলে:

14.6.2 নির্ভরশীল নাম [temp.dep]

অনুচ্ছেদ 3

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

সুতরাং, মনে হচ্ছে যে যখন এমএমভিসি বা কমেও যখন টেম্পলেটটিকে তাত্ক্ষণিক করে তখন কল সাইটটিতে নাম সন্ধান করার toString()সদস্যের ফাংশন বিবেচনা করে T, doToString()তখন এটি ভুল (যদিও এটি প্রকৃতপক্ষে আমি এই ক্ষেত্রে যা খুঁজছি তা)।

জি সি সি এবং ডিজিটাল মঙ্গলের আচরণ সঠিক বলে মনে হচ্ছে - উভয় ক্ষেত্রেই নন-সদস্য toString()ফাংশন কলটিতে আবদ্ধ।

ইঁদুর - আমি ভেবেছিলাম আমি একটি চতুর সমাধান খুঁজে পেয়েছি, পরিবর্তে আমি একটি দম্পতি কম্পাইলার বাগ উন্মোচিত ...

#include <iostream>
#include <string>

struct Hello
{
    std::string toString() {
        return "Hello";
    }
};

struct Generic {};


// the following namespace keeps the toString() method out of
//  most everything - except the other stuff in this
//  compilation unit

namespace {
    std::string toString()
    {
        return "toString not defined";
    }

    template <typename T>
    class optionalToStringImpl : public T
    {
    public:
        std::string doToString() {

            // in theory, the name lookup for this call to 
            //  toString() should find the toString() in 
            //  the base class T if one exists, but if one 
            //  doesn't exist in the base class, it'll 
            //  find the free toString() function in 
            //  the private namespace.
            //
            // This theory works for MSVC (all versions
            //  from VC6 to VC9) and Comeau C++, but
            //  does not work with MinGW 3.4.5 or 
            //  Digital Mars 8.42n
            //
            // I'm honestly not sure what the standard says 
            //  is the correct behavior here - it's sort 
            //  of like ADL (Argument Dependent Lookup - 
            //  also known as Koenig Lookup) but without
            //  arguments (except the implied "this" pointer)

            return toString();
        }
    };
}

template <typename T>
std::string optionalToString(T & obj)
{
    // ugly, hacky cast...
    optionalToStringImpl<T>* temp = reinterpret_cast<optionalToStringImpl<T>*>( &obj);

    return temp->doToString();
}



int
main(int argc, char *argv[])
{
    Hello helloObj;
    Generic genericObj;

    std::cout << optionalToString( helloObj) << std::endl;
    std::cout << optionalToString( genericObj) << std::endl;
    return 0;
}

এখানে অনেকগুলি উত্তর রয়েছে, তবে একটি সংস্করণ খুঁজে বের করতে ব্যর্থ হয়েছে, যা বাস্তব পদ্ধতির রেজোলিউশন ক্রম সঞ্চালন করে , যখন কোনও নতুন সি ++ বৈশিষ্ট্যগুলি ব্যবহার করে না (কেবলমাত্র সি ++ 98 বৈশিষ্ট্যগুলি ব্যবহার করে)।
দ্রষ্টব্য: এই সংস্করণটি পরীক্ষা করা হয়েছে এবং ভিসি ++ 2013, জি ++ 5.2.0 এবং অনলাইন কম্পাইলারের সাথে কাজ করছে।

তাই আমি একটি সংস্করণ নিয়ে এসেছি, যা sizeof () ব্যবহার করে:

template<typename T> T declval(void);

struct fake_void { };
template<typename T> T &operator,(T &,fake_void);
template<typename T> T const &operator,(T const &,fake_void);
template<typename T> T volatile &operator,(T volatile &,fake_void);
template<typename T> T const volatile &operator,(T const volatile &,fake_void);

struct yes { char v[1]; };
struct no  { char v[2]; };
template<bool> struct yes_no:yes{};
template<> struct yes_no<false>:no{};

template<typename T>
struct has_awesome_member {
 template<typename U> static yes_no<(sizeof((
   declval<U>().awesome_member(),fake_void()
  ))!=0)> check(int);
 template<typename> static no check(...);
 enum{value=sizeof(check<T>(0)) == sizeof(yes)};
};


struct foo { int awesome_member(void); };
struct bar { };
struct foo_void { void awesome_member(void); };
struct wrong_params { void awesome_member(int); };

static_assert(has_awesome_member<foo>::value,"");
static_assert(!has_awesome_member<bar>::value,"");
static_assert(has_awesome_member<foo_void>::value,"");
static_assert(!has_awesome_member<wrong_params>::value,"");

লাইভ ডেমো (এক্সটেন্ডেড রিটার্ন টাইপ চেকিং এবং ভিসি ++ 2010 ওয়ার্কারাউন্ড সহ): http://cpp.sh/5b2vs

কোন উত্স, আমি নিজেকে সঙ্গে এসেছিলেন হিসাবে।

G ++ কম্পাইলারে লাইভ ডেমো চালানোর সময়, দয়া করে মনে রাখবেন যে 0 এর অ্যারে মাপ অনুমোদিত, অর্থাত যে static_assert ব্যবহৃত হয়, এটি ব্যর্থ হলে এমনকি একটি কম্পাইলার ত্রুটি ট্রিগার করবে না।
সাধারণভাবে ব্যবহৃত কাজটি প্রায় 'এক্সটার্ন' ম্যাক্রোতে 'টাইপডফ' প্রতিস্থাপন করা।


template<class T>
auto optionalToString(T* obj)
->decltype( obj->toString(), std::string() )
{
     return obj->toString();
}

template<class T>
auto optionalToString(T* obj)
->decltype( std::string() )
{
     throw "Error!";
}




sfinae