it-swarm-id.com

Periksa apakah kelas memiliki fungsi anggota dari tanda tangan yang diberikan

Saya meminta trik template untuk mendeteksi apakah kelas memiliki fungsi anggota tertentu dari tanda tangan yang diberikan.

Masalahnya mirip dengan yang dikutip di sini http://www.gotw.ca/gotw/071.htm tetapi tidak sama: pada item buku Sutter ia menjawab pertanyaan bahwa suatu kelas C HARUS MENYEDIAKAN fungsi anggota dengan tanda tangan tertentu, jika tidak program tidak dapat dikompilasi. Dalam masalah saya, saya perlu melakukan sesuatu jika kelas memiliki fungsi itu, yang lain lakukan "sesuatu yang lain".

Masalah serupa juga dihadapi oleh boost :: serialisasi tetapi saya tidak suka solusi yang mereka adopsi: fungsi templat yang secara default memanggil fungsi bebas (yang harus Anda tetapkan) dengan tanda tangan tertentu kecuali jika Anda menetapkan fungsi anggota tertentu ( dalam kasus mereka "membuat cerita bersambung" yang mengambil 2 parameter dari jenis tertentu) dengan tanda tangan tertentu, jika tidak maka kesalahan kompilasi akan terjadi. Yaitu untuk mengimplementasikan serialisasi baik intrusi maupun non-intrusif.

Saya tidak suka solusi itu karena dua alasan:

  1. Agar tidak mengganggu, Anda harus mengganti fungsi "serialisasi" global yang ada di boost :: serialisasi namespace, sehingga Anda MEMILIKI DALAM KODE KLIEN ANDA untuk membuka boost namespace dan serialisasi namespace!
  2. Tumpukan untuk menyelesaikan kekacauan itu adalah 10 hingga 12 pemanggilan fungsi.

Saya perlu mendefinisikan perilaku khusus untuk kelas yang tidak memiliki fungsi anggota, dan entitas saya berada di dalam ruang nama yang berbeda (dan saya tidak ingin mengganti fungsi global yang didefinisikan dalam satu namespace sementara saya di yang lain)

Bisakah Anda memberi saya petunjuk untuk memecahkan teka-teki ini?

121
ugasoft

Saya tidak yakin apakah saya mengerti Anda dengan benar, tetapi Anda dapat mengeksploitasi SFINAE untuk mendeteksi keberadaan fungsi pada waktu kompilasi. Contoh dari kode saya (menguji apakah kelas memiliki fungsi anggota size_t used_memory () const).

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>());
}
87
yrp

Berikut ini adalah implementasi yang mungkin mengandalkan fitur C++ 11. Itu benar mendeteksi fungsi bahkan jika itu diwarisi (tidak seperti solusi dalam jawaban yang diterima, seperti yang diamati oleh Mike Kinghan jawabannya ).

Fungsi pengujian potongan ini disebut 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;
};

Pemakaian:

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
119
jrok

Jawaban yang diterima untuk pertanyaan introspeksi fungsi anggota ini, meskipun populer, memiliki hambatan yang dapat diamati dalam program berikut:

#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;
}

Dibangun dengan GCC 4.6.3, program mengeluarkan 110 - memberi tahu kami bahwa T = std::shared_ptr<int> Tidak tidak menyediakan int & T::operator*() const.

Jika Anda belum bijaksana dengan gotcha ini, maka melihat definisi std::shared_ptr<T> Di header <memory> Akan menjelaskan. Dalam implementasi itu, std::shared_ptr<T> Diturunkan dari kelas dasar dari mana ia mewarisi operator*() const. Jadi contoh template SFINAE<U, &U::operator*> Yang merupakan "menemukan" operator untuk U = std::shared_ptr<T> Tidak akan terjadi, karena std::shared_ptr<T> Tidak memiliki operator*() dalam hak dan templatnya sendiri Instansiasi tidak "melakukan warisan".

Halangan ini tidak mempengaruhi pendekatan SFINAE yang terkenal, menggunakan "Trik sizeof ()", untuk mendeteksi hanya apakah T memiliki beberapa fungsi anggota mf (lihat mis. jawaban ini --- dan komentar). Tetapi menetapkan bahwa T::mf Ada sering (biasanya?) Tidak cukup baik: Anda mungkin juga perlu memastikan bahwa ia memiliki tanda tangan yang diinginkan. Di situlah skor teknik digambarkan. Varian yang ditandai dari tanda tangan yang diinginkan tertulis dalam parameter tipe templat yang harus dipenuhi oleh &T::mf Agar probe SFINAE berhasil. Namun teknik cetakan template ini memberikan jawaban yang salah ketika T::mf Diwarisi.

Teknik SFINAE yang aman untuk introspeksi kompilasi dari T::mf Harus menghindari penggunaan &T::mf Dalam argumen templat untuk membuat instance tipe yang bergantung pada resolusi templat fungsi SFINAE. Sebagai gantinya, resolusi fungsi templat SFINAE hanya dapat bergantung pada deklarasi tipe yang tepat terkait yang digunakan sebagai tipe argumen dari fungsi probe SFINAE yang kelebihan beban.

Sebagai jawaban atas pertanyaan yang mematuhi batasan ini, saya akan mengilustrasikan untuk deteksi kompiletime dari E T::operator*() const, untuk arbitrary T dan E. Pola yang sama akan berlaku mutatis mutandis untuk menyelidiki tanda tangan metode anggota lainnya.

#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? */
};

Dalam solusi ini, fungsi probe SFINAE yang kelebihan beban test() adalah "dipanggil secara rekursif". (Tentu saja itu tidak benar-benar dipanggil sama sekali; itu hanya memiliki jenis pengembalian pemanggilan hipotetis diselesaikan oleh kompiler.)

Kami perlu menyelidiki setidaknya satu dan paling banyak dua poin informasi:

  • Apakah T::operator*() ada sama sekali? Jika tidak, kita sudah selesai.
  • Mengingat T::operator*() ada, apakah tanda tangannya E T::operator*() const?

Kami mendapatkan jawaban dengan mengevaluasi tipe kembalinya satu panggilan ke test(0,0). Itu dilakukan oleh:

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

Panggilan ini mungkin diselesaikan dengan kelebihan /* SFINAE operator-exists :) */ Dari test(), atau mungkin menyelesaikan ke kelebihan /* SFINAE game over :( */. Itu tidak dapat mengatasi kelebihan /* SFINAE operator-has-correct-sig :) */, Karena argumen itu hanya mengharapkan satu argumen dan kami melewati dua argumen.

Mengapa kita melewati dua? Cukup dengan memaksa resolusi untuk mengecualikan /* SFINAE operator-has-correct-sig :) */. Argumen kedua tidak memiliki arti lain.

Panggilan ke test(0,0) akan menyelesaikan ke /* SFINAE operator-exists :) */ Kalau-kalau argumen pertama 0 mengesahkan tipe parameter pertama dari kelebihan itu, yaitu decltype(&A::operator*), dengan A = T. 0 akan memenuhi jenis itu untuk berjaga-jaga jika T::operator* Ada.

Anggap saja kompiler mengatakan Ya untuk itu. Kemudian ia pergi dengan /* SFINAE operator-exists :) */ Dan perlu menentukan tipe kembali dari panggilan fungsi, yang dalam hal ini adalah decltype(test(&A::operator*)) - jenis kembali panggilan lain ke test().

Kali ini, kami hanya menyampaikan satu argumen, &A::operator*, Yang sekarang kami tahu ada, atau kami tidak akan berada di sini. Panggilan ke test(&A::operator*) mungkin menyelesaikan ke /* SFINAE operator-has-correct-sig :) */ Atau lagi untuk mungkin menyelesaikan ke /* SFINAE game over :( */. Panggilan akan cocok dengan /* SFINAE operator-has-correct-sig :) */ Untuk jaga-jaga &A::operator* Memenuhi jenis parameter tunggal dari kelebihan itu, yaitu E (A::*)() const, dengan A = T.

Kompiler akan mengatakan Ya di sini jika T::operator* Memiliki tanda tangan yang diinginkan, dan sekali lagi harus mengevaluasi tipe pengembalian kelebihan beban. Tidak ada lagi "rekursi" sekarang: itu adalah std::true_type.

Jika kompiler tidak memilih /* SFINAE operator-exists :) */ Untuk panggilan test(0,0) atau tidak memilih /* SFINAE operator-has-correct-sig :) */ Untuk panggilan test(&A::operator*), maka dalam kedua kasus itu berjalan dengan /* SFINAE game over :( */ Dan jenis pengembalian akhir adalah std::false_type.

Berikut adalah program pengujian yang menunjukkan templat yang menghasilkan jawaban yang diharapkan dalam beragam sampel kasus (GCC 4.6.3 lagi).

// 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;
}

Apakah ada kekurangan baru dalam ide ini? Bisakah itu dibuat lebih umum tanpa sekali lagi jatuh dari halangan yang dihindarinya?

35
Mike Kinghan

Berikut beberapa cuplikan penggunaan: * Nyali untuk semua ini lebih jauh ke bawah

Periksa anggota x dalam kelas yang diberikan. Bisa berupa var, func, class, union, atau enum:

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

Periksa fungsi anggota 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;

Periksa variabel anggota x:

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

Periksa kelas anggota x:

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

Periksa serikat anggota x:

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

Periksa anggota enum x:

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

Periksa fungsi anggota x terlepas dari tanda tangan:

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;

OR

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;

Detail dan inti:

/*
    - 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;
};

Makro (El Diablo!):

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)
13
Brett Rossier

Ini harus cukup, jika Anda tahu nama fungsi anggota yang Anda harapkan. (Dalam hal ini, fungsi bla gagal untuk instantiate jika tidak ada fungsi anggota (menulis yang berfungsi tetap sulit karena ada kekurangan spesialisasi fungsi parsial. Anda mungkin perlu menggunakan templat kelas) Juga, memungkinkan struct (yang mirip dengan enable_if) juga dapat digunakan pada jenis fungsi yang Anda inginkan sebagai anggota.

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);
}
11
coppro

Untuk mencapai ini kita harus menggunakan:

  1. Function template overloading dengan tipe pengembalian yang berbeda sesuai dengan apakah metode tersebut tersedia
  2. Sesuai dengan meta-conditional di header type_traits , kami ingin mengembalikan true_type Atau false_type = dari kelebihan kami
  3. Deklarasikan kelebihan true_type Mengharapkan int dan kelebihan false_type Untuk mengeksploitasi Parameter Variadik untuk dieksploitasi: "Prioritas terendah konversi Ellipsis dalam resolusi kelebihan" " =
  4. Dalam mendefinisikan spesifikasi templat untuk fungsi true_type Kita akan menggunakan declval dan decltype memungkinkan kita mendeteksi fungsi tersebut independen dari perbedaan tipe pengembalian atau kelebihan antar metode

Anda dapat melihat contoh langsung dari ini di sini . Tapi saya juga akan menjelaskannya di bawah ini:

Saya ingin memeriksa keberadaan fungsi bernama test yang mengambil tipe convertible dari int, maka saya harus mendeklarasikan dua fungsi ini:

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 is true (Perhatikan tidak perlu membuat fungsi khusus untuk menangani void a::test() overload, void a::test(int) diterima)
  • decltype(hasTest<b>(0))::value adalah true (Karena int dapat dikonversi ke doubleint b::test(double) diterima, terlepas dari jenis pengembalian)
  • decltype(hasTest<c>(0))::value adalah false (c tidak memiliki metode bernama test yang menerima jenis konversi dari int karenanya ini tidak diterima )

Solusi ini memiliki 2 kelemahan:

  1. Membutuhkan deklarasi per metode untuk sepasang fungsi
  2. Menciptakan polusi namespace terutama jika kita ingin menguji untuk nama yang mirip, misalnya apa yang akan kita beri nama fungsi yang ingin menguji untuk metode test()?

Jadi, penting bahwa fungsi-fungsi ini dideklarasikan dalam namespace perincian, atau idealnya jika hanya digunakan dengan kelas, mereka harus dideklarasikan secara pribadi oleh kelas itu. Untuk itu saya telah menulis makro untuk membantu Anda abstrak informasi ini:

#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));

Anda bisa menggunakan ini seperti:

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

Selanjutnya memanggil details::test_int<a>::value Atau details::test_void<a>::value Akan menghasilkan true atau false untuk keperluan kode sebaris atau meta-pemrograman.

5
Jonathan Mee

Ini jawaban sederhana dari jawaban Mike Kinghan. Ini akan mendeteksi metode yang diwarisi. Itu juga akan memeriksa tanda tangan tepat (tidak seperti pendekatan jrok yang memungkinkan konversi argumen).

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, "");

Runnable contoh

5
Valentin Milea

Anda dapat menggunakan std :: is_member_function_pointer

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

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

Datang dengan masalah yang sama sendiri, dan menemukan solusi yang diusulkan di sini sangat menarik ... tetapi memiliki persyaratan untuk solusi yang:

  1. Mendeteksi fungsi yang diwarisi juga;
  2. Kompatibel dengan kompiler non C++ 11 siap (jadi tidak ada jenis tulisan)

Menemukan yang lain tas mengusulkan sesuatu seperti ini, berdasarkan pada diskusi BOOST . Berikut adalah generalisasi dari solusi yang diusulkan sebagai dua deklarasi makro untuk kelas ciri, mengikuti model boost :: has _ * kelas.

#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__

Makro ini diperluas ke kelas ciri dengan prototipe berikut:

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;
};

Jadi apa penggunaan khas yang bisa dilakukan dari ini?

// 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)
}
4
S. Paris

Agar tidak mengganggu, Anda juga dapat menempatkan serialize di namespace dari kelas yang diserialisasi, atau dari kelas arsip, terima kasih kepada pencarian Koenig . Lihat Ruang Nama untuk Override Fungsi Gratis untuk detail lebih lanjut. :-)

Membuka setiap namespace yang diberikan untuk mengimplementasikan fungsi gratis adalah Simply Wrong. (mis., Anda tidak seharusnya membuka namespace std untuk mengimplementasikan swap untuk jenis Anda sendiri, tetapi sebaiknya gunakan pencarian Koenig.)

3

Baik. Percobaan kedua. Tidak apa-apa jika Anda tidak menyukai yang ini juga, saya mencari lebih banyak ide.

Artikel Herb Sutter berbicara tentang sifat-sifat. Jadi Anda dapat memiliki kelas sifat yang instantiasi defaultnya memiliki perilaku mundur, dan untuk setiap kelas di mana fungsi anggota Anda ada, maka kelas sifat khusus untuk menjalankan fungsi anggota. Saya percaya artikel Herb menyebutkan teknik untuk melakukan ini sehingga tidak melibatkan banyak menyalin dan menempel.

Seperti yang saya katakan, mungkin Anda tidak ingin pekerjaan ekstra yang terlibat dengan "penandaan" kelas yang mengimplementasikan anggota itu. Dalam hal ini, saya sedang mencari solusi ketiga ....

2

Tanpa dukungan C++ 11 (decltype) ini mungkin berfungsi:

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;
}

Semoga ini berhasil

A, Aa dan B adalah klausa yang dipermasalahkan, Aa adalah yang spesial yang mewarisi anggota yang kami cari.

Dalam FooFinder the true_type dan false_type adalah pengganti untuk kelas koresponden C++ 11. Juga untuk memahami pemrograman meta templat, mereka mengungkapkan dasar dari trik-ukuran SFINAE.

TypeSink adalah sebuah kerangka templat yang digunakan kemudian untuk menenggelamkan hasil integral dari operator sizeof ke dalam contoh kerangka untuk membentuk suatu jenis.

Fungsi match adalah jenis templat SFINAE lain yang dibiarkan tanpa rekanan generik. Karenanya hanya dapat dipakai jika jenis argumennya cocok dengan jenis yang dikhususkan untuknya.

Kedua fungsi test bersama dengan deklarasi enum akhirnya membentuk pola SFINAE pusat. Ada yang generik menggunakan Ellipsis yang mengembalikan false_type dan rekanan dengan argumen yang lebih spesifik untuk diutamakan.

Untuk dapat membuat instantiate fungsi test dengan argumen templat T, fungsi match harus di-instantiated, karena jenis kembalinya diperlukan untuk membuat instance TypeSink argumen. Peringatan adalah bahwa &U::foo, yang dibungkus dalam argumen fungsi, adalah tidak dirujuk dari dalam spesialisasi argumen templat, jadi pencarian anggota yang diwariskan masih berlangsung.

1
Kamajii

Saya yakin jawaban yang Anda cari ada di sini.

http://www.martinecker.com/wiki/index.php?title=Detecting_the_Existence_of_Operators_at_Compile-Time

dan contoh yang sedikit lebih lengkap di sini

http://pastie.org/298994

Saya menggunakan teknik ini untuk mendeteksi keberadaan operator ostream pendukung << pada kelas yang bersangkutan dan kemudian menghasilkan sedikit kode yang berbeda tergantung.

Saya tidak percaya itu mungkin sebelum menemukan solusi tertaut tetapi itu adalah trik yang sangat rapi. Habiskan waktu untuk memahami kode dan itu sangat berharga saat ini.

Brad

0
Brad Phelan

Jika Anda menggunakan kebodohan Facebook, makro kebodohan di luar kotak untuk membantu Anda:

#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;
}

Meskipun detail implementasi sama dengan jawaban sebelumnya, menggunakan perpustakaan lebih sederhana.

0