是否可以使用SFINAE/模板来检查操作员是否存在? [重复]

问题描述 投票:0回答:3

我试图检查编译时是否存在运算符,如果不存在,我只想忽略它,有什么方法可以做到这一点吗?

运算符示例:

 template <typename T>
 QDataStream& operator<<(QDataStream& s, const QList<T>& l);
c++ templates sfinae
3个回答
12
投票

我最终使用了后备命名空间:

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

还找到了在编译时检查运算符的正确方法(尽管我将使用后备命名空间)。

或多或少基于这个

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

//编辑稍微更改了 has_stream_operators 。

//编辑删除了链接,显然该网站有一些攻击javascript。


6
投票

这是一个老问题,但值得注意的是,Boost 刚刚为几乎所有具有最新“操作符类型特征”的操作符添加了此功能。 OP询问的具体操作员是用boost:has_left_shift进行测试的。

    


2
投票
int*

int*
表示
op<<
,您将在编译时收到硬错误。因此,对于非类类型,您需要过滤掉标准禁止的类型。

对于

op+

我曾经为了好玩写过这样的东西。请注意,我使用的是

C
标头,因为我也需要使用
clang
编译器测试代码,当时该编译器不支持我的 C++ 标头:

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

当然事后做测试很重要

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

© www.soinside.com 2019 - 2024. All rights reserved.