2010-12-13 3 views
13

Estoy intentando verificar si existe un operador en tiempo de compilación, si no es así, solo quiero que se ignore, ¿hay alguna manera de hacerlo?¿Es posible usar SFINAE/templates para verificar si existe un operador?

ejemplo operador:

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 podría o no podría ayudarlo. :) – Kos

+0

Gracias, realmente no ayudó, pero me dio una idea sobre qué buscar. – OneOfOne

+0

@OneOfOne: Esta es la información que los Conceptos tienen la intención de proporcionar; consulte http://www.boost.org/doc/libs/1_45_0/libs/concept_check/concept_check.htm para saber cómo crear el meta operador que comprobará eso (para usar con 'enable_if') –

Respuesta

12

Terminé usando un espacio de nombres de reserva:

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

encuentra también la forma correcta para comprobar si hay operadores en tiempo de compilación (aunque voy con el repliegue espacio de nombres).

más o menos, basado en este:

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

// editar cambió has_stream_operators un poco.

// editar eliminado el enlace, aparentemente el sitio tiene algún ataque javascript.

+0

Si un operador ya estaba definido, ¿no provocará una sobrecarga ambigua? – watson1180

+1

No, ya que es una plantilla, el operador "real" será preferido. – OneOfOne

+1

¿Qué sucede si el operador "real" también es una plantilla? – watson1180

2

No es demasiado fácil y en C++ 03 no es posible en general. Si utiliza int* y int* para op<< por ejemplo, obtendrá un error grave en el momento de la compilación. Por lo tanto, para los tipos que no son de clase, debe filtrar los tipos que prohíbe el estándar.

Para op+ Una vez escribí tal cosa para las patadas. Tenga en cuenta que estoy usando C cabeceras, porque tenía que probar el código con el clang compilador también, que en ese momento no era compatible con mi C++ cabeceras:

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

Por supuesto, es muy importante hacer pruebas después

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

Gracias, pero consulte la otra respuesta a continuación. – OneOfOne

+0

@OneOfOne Ya he comprobado su respuesta anterior :) –

5

ésta es una vieja pregunta, pero vale la pena señalar que Boost acaba de añadir esta capacidad para casi todos los operadores con su nuevo Operator Type Traits. El operador específico sobre el que OP ha preguntado se ha probado con boost:has_left_shift.

+0

Comprobé esto, error esta clase está plagada de todos los mismos errores y problemas que todas las otras soluciones que he visto y probado. :( –

+0

@AscensionSystems ¿Podría ser más específico? – histumness

+0

Simplemente busque la documentación en él. Enumera algunos problemas conocidos que básicamente lo hacen inútil debido a la naturaleza de los problemas. –

Cuestiones relacionadas