2010-12-13 5 views
13

Ich versuche zu prüfen, ob ein Operator zur Kompilierzeit existiert, wenn es nicht will ich es einfach ignoriert, gibt es eine Möglichkeit, das zu tun?Ist es möglich, mit SFINAE/Templates zu prüfen, ob ein Operator existiert?

Beispiel Betreiber:

template <typename T> 
QDataStream& operator<<(QDataStream& s, const QList<T>& l); 
+0

http://www.boost.org/doc/libs/1_39_0/libs/type_traits/doc/html/index.html kann oder kann nicht helfen. :) – Kos

+0

Danke, hat nicht wirklich geholfen, aber gab mir eine Idee, wonach ich suchen sollte. – OneOfOne

+0

@OneOfOne: Dies ist die Information, die Concepts zu bieten beabsichtigen, überprüfen Sie http://www.boost.org/doc/libs/1_45_0/libs/concept_check/concept_check.htm, um zu erfahren, wie Sie den Meta-Operator erstellen, der überprüft das (für die Verwendung mit 'enable_if') –

Antwort

12

ich eine Rückfall-Namespace endete:

namespace operators_fallback { 
template <typename T> 
inline QDataStream& operator<<(QDataStream& s, const T &) { return s; } 

template <typename T> 
inline QDataStream& operator>>(QDataStream& s, T &) { return s; } 

template <typename T> 
inline QDebug operator<<(QDebug d, const T &) { return d; } 
}; 

... 
inline void load(QDataStream & s) { 
    using namespace operator_fallback; 
    s >> item; 
} 

auch den richtigen Weg für die Betreiber bei der Kompilierung zu überprüfen gefunden (obwohl ich mit dem Rückfall werde Namensraum).

mehr oder weniger, bezogen auf diese:

namespace private_impl { 
    typedef char yes; 
typedef char (&no)[2]; 

struct anyx { template <class T> anyx(const T &); }; 

no operator << (const anyx &, const anyx &); 
no operator >> (const anyx &, const anyx &); 


template <class T> yes check(T const&); 
no check(no); 

template <typename StreamType, typename T> 
struct has_loading_support { 
    static StreamType & stream; 
    static T & x; 
    static const bool value = sizeof(check(stream >> x)) == sizeof(yes); 
}; 

template <typename StreamType, typename T> 
struct has_saving_support { 
    static StreamType & stream; 
    static T & x; 
    static const bool value = sizeof(check(stream << x)) == sizeof(yes); 
}; 

template <typename StreamType, typename T> 
struct has_stream_operators { 
    static const bool can_load = has_loading_support<StreamType, T>::value; 
    static const bool can_save = has_saving_support<StreamType, T>::value; 
    static const bool value = can_load && can_save; 
}; 
} 
template<typename T> 
struct supports_qdatastream : private_impl::has_stream_operators<QDataStream, T> {}; 

template<typename T> 
struct can_load : private_impl::has_loading_support<QDataStream, T> {}; 

template<typename T> 
struct can_save : private_impl::has_saving_support<QDataStream, T> {}; 

template<typename T> 
struct can_debug : private_impl::has_saving_support<QDebug, T> {}; 

// bearbeiten has_stream_operators ein wenig verändert.

// bearbeiten entfernt den Link, anscheinend hat die Website einige Angriffs-Javascript.

+0

Wenn bereits ein Operator definiert wurde, führt dies nicht zu einer mehrdeutigen Überladung? – watson1180

+1

Nein, da es eine Vorlage ist, wird der "echte" Operator bevorzugt. – OneOfOne

+1

Was ist, wenn der "echte" Operator auch eine Vorlage ist? – watson1180

2

Es ist nicht zu einfach und in C++ 03 ist es im Allgemeinen nicht möglich. Wenn Sie zum Beispiel int* und int* für op<< verwenden, erhalten Sie einen schweren Fehler zur Kompilierzeit. Bei Nicht-Klassen-Typen müssen Sie die Typen filtern, die der Standard verbietet.

Für op+ schrieb ich einmal so etwas für die Kicks. Beachten Sie, dass ich C Header bin mit, weil ich zu dem Code mit dem clang Compiler zu testen benötigt, was zu dieser Zeit nicht meine C++ Header unterstützen:

#include <stddef.h> 
#include <stdio.h> 

namespace detail { 
struct any { 
    template<typename T> any(T const&); 
}; 
struct tag { char c[2]; }; 

int operator,(detail::tag, detail::tag); 
template<typename T> void operator,(detail::tag, T const&); 
char operator,(int, detail::tag); 
} 

namespace fallback { 
    detail::tag operator+(detail::any const&, detail::any const&); 
} 

namespace detail { 
template<typename T> 
struct is_class { 
    typedef char yes[1]; 
    typedef char no[2]; 

    template<typename U> 
    static yes &check(int U::*); 
    template<typename U> 
    static no &check(...); 

    static bool const value = sizeof check<T>(0) == 1; 
}; 

template<typename T> 
struct is_pointer { typedef T pointee; static bool const value = false; }; 
template<typename T> 
struct is_pointer<T*> { typedef T pointee; static bool const value = true; }; 

template<typename T, typename U> 
struct is_same { 
    static bool const value = false; 
}; 

template<typename T> 
struct is_same<T, T> { 
    static bool const value = true; 
}; 

template<typename T> 
struct is_incomplete_array { 
    static bool const value = false; 
}; 

template<typename T> 
struct is_incomplete_array<T[]> { 
    static bool const value = true; 
}; 

template<typename T> 
struct is_reference { 
    typedef T referee; 
    static bool const value = false; 
}; 

template<typename T> 
struct is_reference<T&> { 
    typedef T referee; 
    static bool const value = true; 
}; 

// is_fn checks whether T is a function type 
template<typename T> 
struct is_fn { 
    typedef char yes[1]; 
    typedef char no[2]; 

    template<typename U> 
    static no &check(U(*)[1]); 

    template<typename U> 
    static yes &check(...); 

    // T not void, not class-type, not U[], U& and T[] invalid 
    // => T is function type 
    static bool const value = 
    !is_same<T const volatile, void>::value && 
    !is_class<T>::value && 
    !is_incomplete_array<T>::value && 
    !is_reference<T>::value && 
    (sizeof check<T>(0) == 1); 
}; 

template<typename T, bool = is_fn<T>::value> 
struct mod_ty { 
    typedef T type; 
}; 

template<typename T> 
struct mod_ty<T, true> { 
    typedef T *type; 
}; 

template<typename T> 
struct mod_ty<T[], false> { 
    typedef T *type; 
}; 

template<typename T, size_t N> 
struct mod_ty<T[N], false> { 
    typedef T *type; 
}; 

// Standard says about built-in +: 
// 
// For addition, either both operands shall have arithmetic or enumeration type, 
// or one operand shall be a pointer to a completely defined object type and 
// the other shall have integral or enumeration type. 

template<typename T> struct Ty; // one particular type 
struct P; // pointer 
struct Nc; // anything nonclass 
struct A; // anything 
struct Fn; // function pointer 

// matches category to type 
template<typename C, typename T, 
     bool = is_pointer<T>::value, 
     bool = !is_class<T>::value, 
     bool = is_fn<typename is_pointer<T>::pointee>::value> 
struct match { 
    static bool const value = false; 
}; 

// one particular type 
template<typename T, bool P, bool Nc, bool Fn> 
struct match<Ty<T const volatile>, T, P, Nc, Fn> { 
    static bool const value = false; 
}; 

// pointer 
template<typename T, bool F> 
struct match<P, T, true, true, F> { 
    static bool const value = true; 
}; 

// anything nonclass 
template<typename T, bool P, bool Fn> 
struct match<Nc, T, P, true, Fn> { 
    static bool const value = true; 
}; 

// anything 
template<typename T, bool P, bool Nc, bool Fn> 
struct match<A, T, P, Nc, Fn> { 
    static bool const value = true; 
}; 

// function pointer 
template<typename T> 
struct match<Fn, T, true, true, true> { 
    static bool const value = true; 
}; 

// one invalid combination 
template<typename A, typename B> 
struct inv; 

// a list of invalid combinations, terminated by B = void 
template<typename A, typename B> 
struct invs; 

// T[] <=> T[N] => T* 
// void() => void(*)() 
// T& => T 
// trying to find all invalid combinations 
// for built-in op+ 
typedef 
invs< 
    inv<Ty<float const volatile>, P>, 
invs< 
    inv<Ty<double const volatile>, P>, 
invs< 
    inv<Ty<long double const volatile>, P>, 
invs< 
    inv<Ty<void * const volatile>, Nc>, 
invs< 
    inv<Ty<void const* const volatile>, Nc>, 
invs< 
    inv<Ty<void volatile* const volatile>, Nc>, 
invs< 
    inv<Ty<void const volatile* const volatile>, Nc>, 
invs< 
    inv<Fn, Nc>, 
invs< 
    inv<Ty<void const volatile>, A>, 
invs< 
    inv<P, P>, 
void 
> > > > > > > > > > invalid_list; 

// match condition: only when ECond<true> is passed by specialization, 
// then it will be selected. 
template<bool> struct ECond; 

template<typename L, typename T, typename U, typename = ECond<true> > 
struct found_impl; 

// this one will first modify the input types to be plain pointers 
// instead of array or function types. 
template<typename L, typename T, typename U> 
struct found : found_impl<L, 
          typename mod_ty< 
          typename is_reference<T>::referee>::type, 
          typename mod_ty< 
          typename is_reference<U>::referee>::type> 
{ }; 

// match was found. 
template<typename F, typename B, typename R, typename T, typename U> 
struct found_impl<invs<inv<F, B>, R>, T, U, 
        ECond<(match<F, T>::value && match<B, U>::value) || 
         (match<B, T>::value && match<F, U>::value)> > { 
    static bool const value = true; 
}; 

// recurse (notice this is less specialized than the previous specialization) 
template<typename H, typename R, typename T, typename U, typename Ec> 
struct found_impl< invs<H, R>, T, U, Ec > : found_impl<R, T, U> { 
}; 

// we hit the end and found nothing 
template<typename T, typename U, typename Ec> 
struct found_impl< void, T, U, Ec > { 
    static bool const value = false; 
}; 

using namespace fallback; 

template<typename T, typename U, 
     bool found_invalid = found<invalid_list, T, U>::value> 
struct is_addable { 
    static T t; 
    static U u; 
    static bool const value = sizeof (detail::tag(), (t+u), detail::tag()) != 1; 
}; 

template<typename T, typename U> 
struct is_addable<T, U, true> { 
    static bool const value = false; 
}; 

} 

template<typename T, typename U> struct is_addable { 
    static bool const value = detail::is_addable<T, U>::value; 
}; 

Natürlich ist es sehr wichtig, Tests zu tun danach

// this one can be added 
struct test { 
    test operator+(test) { return(*this); } 
}; 

// this one cannot be added 
struct nono { }; 

// this fails because of an ambiguity, because there is a comma 
// operator taking a variable parameter on its left hand side.  
struct fails { fails operator+(fails); }; 

template<typename T> 
void operator,(T const&, fails); 


int main() { 
    printf("%d\n", is_addable<test, test>::value); 
    printf("%d\n", is_addable<int, float>::value); 
    printf("%d\n", is_addable<nono, nono>::value); 
    printf("%d\n", is_addable<int*, int>::value); 
    printf("%d\n", is_addable<int[1], int>::value); 
    printf("%d\n", is_addable<int[1], float[2]>::value); 
    printf("%d\n", is_addable<int*, float*>::value); 
    printf("%d\n", is_addable<void*, float>::value); 
    printf("%d\n", is_addable<void, int>::value); 
    printf("%d\n", is_addable<void(), int>::value); 
    printf("%d\n", is_addable<int, void(**)()>::value); 
    printf("%d\n", is_addable<float*&, int*&>::value); 
} 
+0

Danke, aber bitte überprüfen Sie die andere Antwort unten. – OneOfOne

+0

@OneOfOne Ich habe Ihre Antwort bereits oben überprüft :) –

5

Dies ist eine alte Frage, aber es ist erwähnenswert, dass Boost-nur hinzugefügt, um diese Fähigkeit für fast alle Betreiber mit ihrem neuesten Operator Type Traits Bemerken. Der spezifische Operator OP, der gefragt wird, wird mit boost:has_left_shift getestet.

+0

Ich habe dies überprüft, Fehler in dieser Klasse ist mit den gleichen Bugs und Probleme wie alle anderen Workarounds, die ich gesehen und ausprobiert. :( –

+0

@AscensionSystems Konnten Sie spezifischer sein? – histumness

+0

Schauen Sie einfach die Dokumentation darüber. Es listet ein paar bekannte Probleme, die es im Grunde wegen der Art der Probleme nutzlos machen. –