Membaca pertanyaan terkait "Cara memanggil anggota berfungsi hanya jika objek kebetulan memilikinya?" dan "Apakah mungkin menulis template C++ untuk memeriksa keberadaan suatu fungsi?", saya mengimplementasikan kelas ciri saya sendiri. Tujuannya sangat sederhana, meskipun saya tidak dapat mencapai apa yang saya inginkan: menyediakan kelas ciri yang secara statis mengalihkan panggilan ke kelas yang cocok.
Jadi, jika kelas yang saya berikan pada ciri-ciri saya memiliki, misalnya metode void open_file()
, ia akan memanggilnya, jika tidak, gunakan fungsi ciri-ciri (yang NOP
, tetapi sekarang merupakan keluaran). Jelas sekali, ini adalah tugas SFINAE, namun karena tidak terlalu familiar dengan prosesnya, saya mengikuti ide-idenya, seperti yang akan Anda lihat.
Semuanya berfungsi dengan baik untuk void open_file()
, tetapi ketika mencoba void open_file(int)
, tidak cocok dan memanggil fungsi NOP
. Ini adalah upaya saya (hampir kata demi kata dari dua pertanyaan itu!):
template <class Type>
class my_traits
{
//! Implements a type for "true"
typedef struct { char value; } true_class;
//! Implements a type for "false"
typedef struct { char value[2]; } false_class;
//! This handy macro generates actual SFINAE class members for checking event callbacks
#define MAKE_MEMBER(X) \
public: \
template <class T> \
static true_class has_##X(decltype(&T::X)); \
\
template <class T> \
static false_class has_##X(...); \
public: \
static constexpr bool call_##X = sizeof(has_##X<Type>(0)) == sizeof(true_class);
MAKE_MEMBER(open_file)
public:
/* SFINAE foo-has-correct-sig :) */
template<class A, class Buffer>
static std::true_type test(void (A::*)(int) const)
{
return std::true_type();
}
/* SFINAE foo-exists :) */
template <class A>
static decltype(test(&A::open_file)) test(decltype(&A::open_file), void *)
{
/* foo exists. What about sig? */
typedef decltype(test(&A::open_file)) return_type;
return return_type();
}
/* SFINAE game over :( */
template<class A>
static std::false_type test(...)
{
return std::false_type();
}
/* This will be either `std::true_type` or `std::false_type` */
typedef decltype(test<Type>(0, 0)) type;
static const bool value = type::value; /* Which is it? */
/* `eval(T const &,std::true_type)`
delegates to `T::foo()` when `type` == `std::true_type`
*/
static void eval(Type const & t, std::true_type)
{
t.open_file();
}
/* `eval(...)` is a no-op for otherwise unmatched arguments */
static void eval(...)
{
// This output for demo purposes. Delete
std::cout << "open_file() not called" << std::endl;
}
/* `eval(T const & t)` delegates to :-
- `eval(t,type()` when `type` == `std::true_type`
- `eval(...)` otherwise
*/
static void eval(Type const &t)
{
eval(t, type());
}
};
class does_match
{
public:
void open_file(int i) const { std::cout << "MATCHES!" << std::endl; };
};
class doesnt_match
{
public:
void open_file() const { std::cout << "DOESN'T!" << std::endl; };
};
Seperti yang Anda lihat, saya telah menerapkan keduanya, yang pertama dengan makro MAKE_MEMBER
hanya memeriksa keberadaan anggota, dan berhasil. Selanjutnya, saya mencoba menggunakannya untuk SFINAE if/else
statis, yaitu, jika ada fungsi anggota, panggil saja, jika tidak gunakan fungsi yang telah ditentukan sebelumnya, tidak berhasil (seperti yang saya katakan, saya tidak terlalu mendalami ke dalam SFINAE).
Upaya kedua hampir sama persis dengan pertanyaan periksa tanda tangan dan keberadaan, namun saya telah memodifikasinya untuk menangani parameter. Namun, ini tidak akan berhasil:
does_match it_does;
doesnt_match it_doesnt;
my_traits<decltype(it_does)>::eval(it_does);
my_traits<decltype(it_doesnt)>::eval(it_doesnt);
// OUTPUT:
// open_file() not called
// open_file() not called
Jelas, ada masalah di sini: Saya tidak memberikan parameter, tetapi saya tidak tahu bagaimana melakukannya.
Saya mencoba memahami dan mempelajari, juga, dapatkah saya menggunakan open_file()
yang bergantung pada parameter templat, misalnya memiliki SFINAE yang cocok dengan template <class T> open_file(T t)
?
Terima kasih & bersorak!