2010-12-13 112 views
13

我想檢查運算符是否存在於編譯時,如果它不是我只是想讓它忽略,有沒有辦法做到這一點?是否可以使用SFINAE /模板來檢查運營商是否存在?

例如運營商:

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可能或可能不會幫助你。 :) – Kos 2010-12-13 23:45:12

+0

謝謝,沒有真正的幫助,但給了我一個想法什麼搜索。 – OneOfOne 2010-12-14 05:34:22

+0

@OneOfOne:這是概念打算提供的信息,請查看http://www.boost.org/doc/libs/1_45_0/libs/concept_check/concept_check.htm瞭解如何創建將檢查的元運算符(與'enable_if'配合使用) – 2010-12-14 07:35:00

回答

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。

+0

如果已經定義了一個運算符,是不是會導致模糊的超載? – watson1180 2010-12-14 03:26:32

+1

不,由於它是一個模板,所以「真正的」操作符將是首選。 – OneOfOne 2010-12-14 05:03:35

+1

如果「真實」運算符也是模板會怎樣? – watson1180 2010-12-14 05:41:59

2

這不是太容易,而且在C++ 03中它不是一般可能的。例如,如果您使用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); 
} 
+0

謝謝,但請檢查下面的其他答案。 – OneOfOne 2010-12-16 09:24:27

+0

@OneOfOne我已經在上面檢查了你的答案:) – 2010-12-16 12:07:35

5

這是一個老問題,但值得注意的只是增加這種能力對於幾乎所有的運營商是加強與他們的最新Operator Type Traits。 OP詢問的具體運營商是使用boost:has_left_shift進行測試。

+0

我檢查了這個錯誤,這個類充滿了與我所見過和嘗試的所有其他解決方法相同的錯誤和問題。 :( – 2012-02-24 07:40:21

+0

@AscensionSystems你能更具體嗎? – histumness 2012-02-24 14:11:14

+0

只需查看它的文檔,它列出了幾個已知的問題,因爲問題的性質,它們基本上沒用。 – 2012-02-24 14:47:35

相關問題