2016-03-30 14 views
7

Let seAltri modi di controllare se una classe ha una certa funzione di membro di controllo

struct Thing { 
    int foo(double, bool) {return 0;} 
}; 

ha la funzione int foo(double, bool) membro durante la fase di compilazione. Ci sono molti modi per farlo e la maggior parte sono solo variazioni di altri. Qualcuno può pensare ad un modo molto diverso (o almeno abbastanza creativo) rispetto ai 5 modi che menziono qui? Sto solo cercando di imparare alcune nuove tecniche con i modelli e SFINAE.

#include <iostream> 
#include <type_traits> 

// Using void_t (this includes using std::is_detected). 
template <typename T> 
using void_t = void; 

template <typename T, typename = void> 
struct has_foo : std::false_type {}; 

template <typename T> 
struct has_foo<T, 
     void_t<decltype(static_cast<int>(std::declval<T>().foo(double{}, bool{})))> 
    > : std::true_type {}; 

// Using the ... default argument. 
template <typename T> 
struct hasfoo { 
    template <typename U> 
    static std::true_type test (decltype(static_cast<int(T::*)(double, bool)>(&T::foo))*); // or 'decltype(static_cast<int>(std::declval<U>().foo(double{}, bool{})))*' works fine too. 

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

    static constexpr bool value = decltype(test<T>(nullptr))::value; 
}; 

// Overloads and trailing return types. 
template <typename> 
struct Helper : std::true_type {}; 

template <typename T> 
auto helper(int) -> Helper<decltype(static_cast<int>(std::declval<T>().foo(double{}, bool{})))>; 

template <typename> 
std::false_type helper(long); 

template <typename T> 
constexpr bool hasFoo() {return decltype(helper<T>(0))::value;} 

// Comma operator (basically the same as the above). 
template <typename T> 
auto check(int) -> decltype(static_cast<int>(std::declval<T>().foo(double{}, bool{})), std::true_type{}); 

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

template <typename T> 
using HasFoo = decltype(check<T>(0)); 

// Member function pointer template parameter. 
template <typename T> 
struct Hasfoo { 
    template <typename U, int(U::*)(double, bool)> 
    struct Tag; 

    template <typename U> 
    static constexpr bool test (Tag<U, &U::foo>*) {return true;} 

    template <typename> 
    static constexpr bool test (...) {return false;} 

    static constexpr bool value = test<T>(nullptr); 
}; 

// Tests 
struct Thing { 
    int foo(double, bool) {return 0;} 
}; 

int main() { 
    static_assert (has_foo<Thing>::value, ""); 
    static_assert (hasfoo<Thing>::value, ""); 
    static_assert (hasFoo<Thing>(), ""); 
    static_assert (HasFoo<Thing>::value, ""); 
} 

Edit: Ho appena ricordato una soluzione elegante e più generale che Yakk ha dato ad una domanda diversa un po 'di tempo fa (ecco la sua digitazione attuale, modificato solo per abbinare la funzione foo):

namespace meta { 
    namespace details { 
    template<template<class...>class Z, class=void, class...Ts> 
    struct can_apply : std::false_type {}; 
    template<template<class...>class Z, class...Ts> 
    struct can_apply<Z, decltype((void)(std::declval<Z<Ts...>>())), Ts...>: 
     std::true_type 
    {}; 
    } 
    template<template<class...>class Z, class...Ts> 
    using can_apply = details::can_apply<Z,void,Ts...>; 
} 

template<class T> 
using member_foo = decltype(static_cast<int(T::*)(double, bool)>(&T::foo)); 

template<class T> 
using has_member_foo = meta::can_apply<member_foo, T>; 
+1

Delle quattro opzioni elencate, solo 'hasfoo' rileverà un cambiamento da' int foo (doppia, bool) {return 0; } 'to' int foo (double, double) {return 0; } 'per esempio. – wally

+0

Hmm ... Immagino di non preoccuparmi troppo delle conversioni implicite. Ma 'decltype (static_cast (& T :: foo)) *' potrebbe essere usato per tutti loro allora. È ciò che circonda quell'espressione di cui sono curioso. – prestokeys

+1

'modello concetto boo foo_double_bool = richiede (T t) {t.foo (0.0, true); }; '. Quanti modi diversi di dire la stessa cosa hai bisogno? –

risposta

1

Qualcuno può pensare a un modo molto diverso (o almeno abbastanza creativo) rispetto ai 5 modi che menziono qui?

Un modo abbastanza creativo per farlo potrebbe essere quello sotto.
Si basa sull'operatore sizeof e un banale utilizzando la dichiarazione (qui denominato Type).
SFINAE e specializzazione modello parziale fanno il resto.

Ne consegue una minima, esempio di lavoro:

#include<type_traits> 
#include<cstddef> 

template<typename T, std::size_t> 
using Type = T; 

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

template<typename T> 
struct U<T, Type<T, sizeof(static_cast<void(T::*)(int)>(&T::f))>>: std::true_type {}; 

struct S { void f(int) {} }; 
struct R { int f(double) { return 42; } }; 
struct T {}; 

int main() { 
    static_assert(U<S>::value, "!"); 
    static_assert(not U<R>::value, "!"); 
    static_assert(not U<T>::value, "!"); 
} 
1

Qualcuno può pensare a un modo che è molto diverso (o almeno abbastanza creativo) rispetto ai 5 modi che ho citato qui?

Un modo abbastanza creativo per farlo potrebbe essere quello sotto.
Si basa sull'operatore noexcept e un banale utilizzando la dichiarazione (qui denominato Type).
SFINAE e specializzazione modello parziale fanno il resto.

Ne consegue una minima, esempio di funzionamento:

#include<type_traits> 
#include<utility> 

template<typename T, bool> 
using Type = T; 

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

template<typename T> 
struct U<T, Type<T, noexcept(std::declval<T>().f(42))>>: std::true_type {}; 

struct S { void f(int) {} }; 
struct T {}; 

int main() { 
    static_assert(U<S>::value, "!"); 
    static_assert(not U<T>::value, "!"); 
} 

Questa soluzione ha un problema rispetto agli altri.
Come esempio, la struct sotto passerebbe il test così:

struct R { int f(double) {} }; 

In altri termini, finché la funzione da testare accetta un argomento del tipo di cui 42 può essere fusa e non importa qual è il tipo di ritorno, è accettato.

1

Qualcuno può pensare a un modo molto diverso (o almeno abbastanza creativo) rispetto ai 5 modi che menziono qui?

Un modo abbastanza creativo per farlo potrebbe essere quello sotto.
Non sfrutta alcun contesto non valutato.Invece, si basa su un banale usando la dichiarazione (qui chiamato Type) e questo è tutto.
SFINAE e specializzazione modello parziale fanno il resto.

Ne consegue una minima, esempio di lavoro:

#include<type_traits> 

template<typename T, void(T::*)(int)> 
using Type = T; 

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

template<typename T> 
struct U<T, Type<T, &T::f>>: std::true_type {}; 

struct S { void f(int) {} }; 
struct R { int f(double) { return 42; } }; 
struct T {}; 

int main() { 
    static_assert(U<S>::value, "!"); 
    static_assert(not U<R>::value, "!"); 
    static_assert(not U<T>::value, "!"); 
} 
1

Qualcuno può pensare a un modo che è molto diverso (o almeno abbastanza creativo) rispetto ai 5 modi che ho citato qui?

Un modo abbastanza creativo per farlo potrebbe essere quello sotto.
Si basa su modello di funzione e sovraccarico. Se sai come funziona l'invio dei tag, dovrebbe essere abbastanza semplice da capire.

Ne consegue una minima, esempio di lavoro:

#include<type_traits> 
#include<cstddef> 

template<typename T, void(T::*)(int) = &T::f> 
constexpr std::true_type f(int) { return {}; } 

template<typename T> 
constexpr std::false_type f(char) { return {}; } 

template<typename T> 
constexpr auto detect() { return f<T>(0); } 

struct S { void f(int) {} }; 
struct R { int f(double) { return 42; } }; 
struct T {}; 

int main() { 
    static_assert(detect<S>(), "!"); 
    static_assert(not detect<R>(), "!"); 
    static_assert(not detect<T>(), "!"); 
} 
0

Qualcuno può pensare a un modo che è molto diverso (o almeno abbastanza creativo) rispetto ai 5 modi che ho citato qui?

Se è possibile utilizzare C++ 14, un altro modo per verificare se una classe ha una variabile membro è tramite le variabili del modello.
Per fare un esempio:

template<typename T, typename = void> 
constexpr bool has_foo = false; 

template<typename T> 
constexpr bool has_foo<T, decltype(std::declval<T>().foo(), void())> = true; 

Credo che è probabilmente una delle soluzioni più compatta, almeno.

Ne consegue una minima, esempio di lavoro:

#include<utility> 

template<typename T, typename = void> 
constexpr bool has_f = false; 

template<typename T> 
constexpr bool has_f<T, decltype(std::declval<T>().f(0), void())> = true; 

struct S { void f(int) {} }; 
struct T {}; 

int main() { 
    static_assert(has_f<S>, "!"); 
    static_assert(not has_f<T>, "!"); 
} 
Problemi correlati