আমি আরও টেম্পলেট মেটা-প্রোগ্রামিংয়ে যেতে চাই। আমি জানি যে SFINAE এর অর্থ দাঁড়ায় "প্রতিস্থাপন ব্যর্থতা কোনও ত্রুটি নয়" " তবে কেউ কি আমাকে SFINAE এর জন্য ভাল ব্যবহার দেখাতে পারে?
আমি আরও টেম্পলেট মেটা-প্রোগ্রামিংয়ে যেতে চাই। আমি জানি যে SFINAE এর অর্থ দাঁড়ায় "প্রতিস্থাপন ব্যর্থতা কোনও ত্রুটি নয়" " তবে কেউ কি আমাকে SFINAE এর জন্য ভাল ব্যবহার দেখাতে পারে?
উত্তর:
এখানে একটি উদাহরণ ( এখানে থেকে ):
template<typename T>
class IsClassT {
private:
typedef char One;
typedef struct { char a[2]; } Two;
template<typename C> static One test(int C::*);
// Will be chosen if T is anything except a class.
template<typename C> static Two test(...);
public:
enum { Yes = sizeof(IsClassT<T>::test<T>(0)) == 1 };
enum { No = !Yes };
};
যখন IsClassT<int>::Yes
মূল্যায়ন করা হয়, 0 তে রূপান্তর করা int int::*
যায় না কারণ কোন শ্রেণি নয়, সুতরাং এর কোনও সদস্য পয়েন্টার থাকতে পারে না। যদি SFINAE না থাকে, তবে আপনি একটি সংকলক ত্রুটি পেয়ে যাবেন, '0 এর মতো কিছু অ-শ্রেণীর টাইপ ইন্টের জন্য সদস্য পয়েন্টারে রূপান্তরিত হতে পারে না'। পরিবর্তে, এটি কেবল ...
ফর্মটি ব্যবহার করে যা দুটি ফেরত দেয়, এবং সুতরাং এটি মিথ্যাতে মূল্যায়ন করে, কোন শ্রেণীর ধরণের নয়।
...
, বরং এটি ছিল int C::*
যা আমি কখনও দেখিনি এবং সন্ধান করতে হবে। জন্য উত্তর পাওয়া কি যে আর কোনটা তা এখানে জন্য ব্যবহার করা যেতে পারে: stackoverflow.com/questions/670734/...
আমি SFINAE
বুলিয়ান শর্ত পরীক্ষা করতে ব্যবহার করতে চাই ।
template<int I> void div(char(*)[I % 2 == 0] = 0) {
/* this is taken when I is even */
}
template<int I> void div(char(*)[I % 2 == 1] = 0) {
/* this is taken when I is odd */
}
এটি বেশ কার্যকর হতে পারে। উদাহরণস্বরূপ, আমি অপারেটর কমা ব্যবহার করে সংগ্রহ করা সূচনা তালিকা স্থির আকারের চেয়ে বেশি কিনা তা পরীক্ষা করতে এটি ব্যবহার করেছি
template<int N>
struct Vector {
template<int M>
Vector(MyInitList<M> const& i, char(*)[M <= N] = 0) { /* ... */ }
}
তালিকাটি কেবল তখনই গ্রহণ করা হয় যখন এম এন এর চেয়ে ছোট হয়, যার অর্থ যে আরম্ভকারী তালিকায় খুব বেশি উপাদান নেই।
বাক্য গঠনটির char(*)[C]
অর্থ: উপাদান টাইপের চর এবং আকারের সাথে একটি অ্যারেতে পয়েন্টার C
। যদি C
মিথ্যা হয় (0 এখানে), তবে আমরা অবৈধ প্রকারটি পাই char(*)[0]
, একটি শূন্য আকারের অ্যারেটির পয়েন্টার: SFINAE এটি তৈরি করে যাতে টেমপ্লেটটিকে এড়ানো হবে।
এর সাথে প্রকাশিত boost::enable_if
, এটি দেখতে এটির মতোই
template<int N>
struct Vector {
template<int M>
Vector(MyInitList<M> const& i,
typename enable_if_c<(M <= N)>::type* = 0) { /* ... */ }
}
অনুশীলনে, আমি প্রায়শই শর্তাদি একটি দরকারী ক্ষমতা যাচাই করার ক্ষমতা খুঁজে পাই।
M <= N ? 1 : -1
তার পরিবর্তে সম্ভবত কাজ করতে পারে।
int foo[0]
। এটির সমর্থিত আমি অবাক হই না, কারণ এটি খুব কার্যকর " কাঠামোটি 0-দৈর্ঘ্যের অ্যারে দিয়ে শেষ হওয়া" কৌশল ( gcc.gnu.org/onlinesocs/gcc/Zero-Length.html ) এর অনুমতি দেয়।
error C2466: cannot allocate an array of constant size 0
সি ++ এ 11 SFINAE পরীক্ষাগুলি অনেক সুন্দর হয়ে উঠেছে। এখানে সাধারণ ব্যবহারের কয়েকটি উদাহরণ দেওয়া হল:
বৈশিষ্ট্যের উপর নির্ভর করে একটি ফাংশন ওভারলোড চয়ন করুন
template<typename T>
std::enable_if_t<std::is_integral<T>::value> f(T t){
//integral version
}
template<typename T>
std::enable_if_t<std::is_floating_point<T>::value> f(T t){
//floating point version
}
তথাকথিত টাইপ সিঙ্ক আইডিয়োম ব্যবহার করে আপনি কোনও ধরণের সদস্যের কিনা তা পরীক্ষা করার মতো এবং যদি সেই সদস্যটি কোনও নির্দিষ্ট ধরণের হয় তবে যাচাই করার মতো এক ধরণের সুন্দর স্বেচ্ছাচারিত পরীক্ষা করতে পারেন
//this goes in some header so you can use it everywhere
template<typename T>
struct TypeSink{
using Type = void;
};
template<typename T>
using TypeSinkT = typename TypeSink<T>::Type;
//use case
template<typename T, typename=void>
struct HasBarOfTypeInt : std::false_type{};
template<typename T>
struct HasBarOfTypeInt<T, TypeSinkT<decltype(std::declval<T&>().*(&T::bar))>> :
std::is_same<typename std::decay<decltype(std::declval<T&>().*(&T::bar))>::type,int>{};
struct S{
int bar;
};
struct K{
};
template<typename T, typename = TypeSinkT<decltype(&T::bar)>>
void print(T){
std::cout << "has bar" << std::endl;
}
void print(...){
std::cout << "no bar" << std::endl;
}
int main(){
print(S{});
print(K{});
std::cout << "bar is int: " << HasBarOfTypeInt<S>::value << std::endl;
}
এখানে একটি সরাসরি উদাহরণ: http://ideone.com/dHhyHE আমি সম্প্রতি আমার ব্লগে SFINAE এবং ট্যাগ প্রেরণের উপর একটি সম্পূর্ণ বিভাগও লিখেছি (নির্লজ্জ প্লাগ তবে প্রাসঙ্গিক) http://metaporky.blogspot.de/2014/08/ পার্ট 7-স্ট্যাটিক-প্রেরণ-function.html
সি ++ ১৪ হিসাবে নোট করুন এখানে একটি std :: void_t রয়েছে যা মূলত আমার টাইপসিংকের মতোই এখানে।
TypeSinkT<decltype(std::declval<T&>().*(&T::bar))>
জায়গায় এবং তার TypeSinkT<decltype(&T::bar)>
পরে অন্য জায়গায় ব্যবহার করার কোনও কারণ আছে ? এছাড়াও &
প্রয়োজনীয় std::declval<T&>
?
TypeSink
, সি ++ 17 আছে std::void_t
:)
বুস্টের সক্ষম_আইএফ লাইব্রেরিতে এসএফআইএনএ ব্যবহারের জন্য একটি দুর্দান্ত ক্লিন ইন্টারফেস সরবরাহ করা হয়েছে। আমার প্রিয় ব্যবহারের উদাহরণগুলির মধ্যে একটি হ'ল বুস্ট.আইট্রেটর লাইব্রেরিতে। SFINAE পুনরাবৃত্তকারী ধরণের রূপান্তর সক্ষম করতে ব্যবহৃত হয়।
সি ++ 17 সম্ভবত বৈশিষ্ট্যগুলির অনুসন্ধানের জন্য একটি সাধারণ উপায় সরবরাহ করবে। বিশদগুলির জন্য N4502 দেখুন , তবে স্ব-অন্তর্ভুক্ত উদাহরণ হিসাবে নিম্নলিখিতটি বিবেচনা করুন।
এই অংশটি ধ্রুবক অংশ, এটি একটি শিরোনামে রাখুন।
// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf.
template <typename...>
using void_t = void;
// Primary template handles all types not supporting the operation.
template <typename, template <typename> class, typename = void_t<>>
struct detect : std::false_type {};
// Specialization recognizes/validates only types supporting the archetype.
template <typename T, template <typename> class Op>
struct detect<T, Op, void_t<Op<T>>> : std::true_type {};
নিম্নলিখিত উদাহরণটি, N4502 থেকে নেওয়া , ব্যবহার দেখায়:
// Archetypal expression for assignment operation.
template <typename T>
using assign_t = decltype(std::declval<T&>() = std::declval<T const &>())
// Trait corresponding to that archetype.
template <typename T>
using is_assignable = detect<T, assign_t>;
অন্যান্য বাস্তবায়নের তুলনায়, এটি মোটামুটি সহজ: সরঞ্জামের একটি হ্রাসিত সেট ( void_t
এবং detect
) যথেষ্ট। তদতিরিক্ত , এটি জানানো হয়েছিল ( এন 4502 দেখুন ) এটি পূর্ববর্তী পদ্ধতির চেয়ে পরিমাপযোগ্যভাবে আরও দক্ষ (সংকলন-সময় এবং সংকলক মেমরির খরচ) is
এখানে একটি লাইভ উদাহরণ দেওয়া আছে , এতে জিসিসির প্রাক 5.1 এর জন্য বহনযোগ্যতার টুইটগুলি অন্তর্ভুক্ত রয়েছে।
এখানে আরেকটি (প্রয়াত) এর SFINAE উদাহরণস্বরূপ, উপর ভিত্তি করে গ্রেগ রজার্স এর উত্তর :
template<typename T>
class IsClassT {
template<typename C> static bool test(int C::*) {return true;}
template<typename C> static bool test(...) {return false;}
public:
static bool value;
};
template<typename T>
bool IsClassT<T>::value=IsClassT<T>::test<T>(0);
এইভাবে, আপনি শ্রেণীর value
কিনা তা দেখতে মানটির মূল্য পরীক্ষা করতে পারেন T
:
int main(void) {
std::cout << IsClassT<std::string>::value << std::endl; // true
std::cout << IsClassT<int>::value << std::endl; // false
return 0;
}
int C::*
আপনার উত্তরের এই বাক্য গঠনটির অর্থ কী? C::*
প্যারামিটারের নাম কীভাবে হতে পারে?
int C::*
হ'ল একটি int
সদস্য ভেরিয়েবলের পয়েন্টারের প্রকার C
।
এখানে SFINAE এর একটি ভাল নিবন্ধ: সি ++ এর SFINAE ধারণার একটি পরিচিতি: একটি শ্রেণীর সদস্যের সংকলন-সময়ের আত্মপরিচয় ।
এটি নিম্নলিখিত হিসাবে সংক্ষিপ্তসার:
/*
The compiler will try this overload since it's less generic than the variadic.
T will be replace by int which gives us void f(const int& t, int::iterator* b = nullptr);
int doesn't have an iterator sub-type, but the compiler doesn't throw a bunch of errors.
It simply tries the next overload.
*/
template <typename T> void f(const T& t, typename T::iterator* it = nullptr) { }
// The sink-hole.
void f(...) { }
f(1); // Calls void f(...) { }
template<bool B, class T = void> // Default template version.
struct enable_if {}; // This struct doesn't define "type" and the substitution will fail if you try to access it.
template<class T> // A specialisation used if the expression is true.
struct enable_if<true, T> { typedef T type; }; // This struct do have a "type" and won't fail on access.
template <class T> typename enable_if<hasSerialize<T>::value, std::string>::type serialize(const T& obj)
{
return obj.serialize();
}
template <class T> typename enable_if<!hasSerialize<T>::value, std::string>::type serialize(const T& obj)
{
return to_string(obj);
}
declval
এমন একটি ইউটিলিটি যা আপনাকে এমন কোনও ধরণের অবজেক্টকে "জাল রেফারেন্স" দেয় যা সহজেই তৈরি করা যায় না। declval
আমাদের SFINAE নির্মাণের জন্য সত্যই কার্যকর।
struct Default {
int foo() const {return 1;}
};
struct NonDefault {
NonDefault(const NonDefault&) {}
int foo() const {return 1;}
};
int main()
{
decltype(Default().foo()) n1 = 1; // int n1
// decltype(NonDefault().foo()) n2 = n1; // error: no default constructor
decltype(std::declval<NonDefault>().foo()) n2 = n1; // int n2
std::cout << "n2 = " << n2 << '\n';
}
এখানে, আমি পয়েন্টারটি কোনও ফাংশন বা সদস্য শ্রেণি পয়েন্টার কিনা তা নির্ধারণের জন্য টেম্পলেট ফাংশন ওভারলোডিং (সরাসরি SFINAE নয়) ব্যবহার করছি: ( iostream cout / cerr সদস্য ফাংশন পয়েন্টার 1 বা সত্য হিসাবে মুদ্রিত করা সম্ভব? )
#include<iostream>
template<typename Return, typename... Args>
constexpr bool is_function_pointer(Return(*pointer)(Args...)) {
return true;
}
template<typename Return, typename ClassType, typename... Args>
constexpr bool is_function_pointer(Return(ClassType::*pointer)(Args...)) {
return true;
}
template<typename... Args>
constexpr bool is_function_pointer(Args...) {
return false;
}
struct test_debugger { void var() {} };
void fun_void_void(){};
void fun_void_double(double d){};
double fun_double_double(double d){return d;}
int main(void) {
int* var;
std::cout << std::boolalpha;
std::cout << "0. " << is_function_pointer(var) << std::endl;
std::cout << "1. " << is_function_pointer(fun_void_void) << std::endl;
std::cout << "2. " << is_function_pointer(fun_void_double) << std::endl;
std::cout << "3. " << is_function_pointer(fun_double_double) << std::endl;
std::cout << "4. " << is_function_pointer(&test_debugger::var) << std::endl;
return 0;
}
ছাপে
0. false
1. true
2. true
3. true
4. true
কোড, এটা পারে (কম্পাইলার "ভালো" উপর নির্ভর করে হবে) একটি ফাংশন যা সত্য বা মিথ্যা ফিরে আসবে করার জন্য একটি রান টাইম কল উৎপন্ন। আপনি যদি is_function_pointer(var)
কম্পাইল টাইপ (রান সময় কোনও ফাংশন কল করা হয় না) মূল্যায়ন করতে বাধ্য করতে চান , আপনি constexpr
ভেরিয়েবল ট্রিক ব্যবহার করতে পারেন :
constexpr bool ispointer = is_function_pointer(var);
std::cout << "ispointer " << ispointer << std::endl;
সি ++ স্ট্যান্ডার্ড অনুসারে সমস্ত চলকগুলিconstexpr
সংকলনের সময় মূল্যায়নের গ্যারান্টিযুক্ত (সংকলনের সময় একটি সি স্ট্রিংয়ের গণনার দৈর্ঘ্য this এটি কি আসলেই একটি কনস্টেক্সারপ? )।
নিম্নলিখিত ধরণের কোডটি SFINAE ব্যবহার করে সংকলককে কোনও প্রকারের নির্দিষ্ট পদ্ধতি রয়েছে কি না তার উপর ভিত্তি করে একটি ওভারলোড নির্বাচন করতে দেয়:
#include <iostream>
template<typename T>
void do_something(const T& value, decltype(value.get_int()) = 0) {
std::cout << "Int: " << value.get_int() << std::endl;
}
template<typename T>
void do_something(const T& value, decltype(value.get_float()) = 0) {
std::cout << "Float: " << value.get_float() << std::endl;
}
struct FloatItem {
float get_float() const {
return 1.0f;
}
};
struct IntItem {
int get_int() const {
return -1;
}
};
struct UniversalItem : public IntItem, public FloatItem {};
int main() {
do_something(FloatItem{});
do_something(IntItem{});
// the following fails because template substitution
// leads to ambiguity
// do_something(UniversalItem{});
return 0;
}
আউটপুট:
ভাসা: 1 ইন্ট: -১