2012-08-25 52 views
1

下面的代碼適用於gcc 4.7。這個想法是我有這些泛型函數,這些泛型函數在序列,指針,圖簇,對,用戶定義的類型以及其他類型上工作。如果其中一個函數是爲一個類型定義的,那麼全部都應該是。我遇到的問題是確定如何專門化他們。我決定定義一個專門用於每種類型的模板類,實現每個函數,然後定義一個自由函數,將其轉發給課堂實現。專門針對通用類型的功能組

#include <utility> 
#include <vector> 
#include <iterator> 
#include <memory> 
#include <iostream> 
#include <algorithm> 

using namespace std; 

template< class M > struct Mon; 

template< class X, class F, 
      class M = Mon< typename decay<X>::type > > 
auto mon(X&& x, F f) -> decltype(M::mon(declval<X>(),f)) { 
    return M::mon(forward<X>(x), f); 
} 

template< class C > struct IsSeqImpl { 
    // Can only be supported on STL-like sequence types, not pointers. 
    template< class _C > static true_type f(typename _C::iterator*); 
    template< class _C > static false_type f(...); 

    typedef decltype(f<C>(0)) type; 
}; 

template< class C > struct IsSeq : public IsSeqImpl<C>::type { }; 

/* Enable if is an STL-like sequence. */ 
template< class C, class R > struct ESeq : std::enable_if<IsSeq<C>::value,R> { }; 

template< class Seq > 
struct Mon : ESeq< Seq, Seq >::type 
{ 
    template< class S, class F > 
    static S mon(const S& s, F f) { 
     S r; 
     transform(begin(s), end(s), back_inserter(r), f); 
     return r; 
    } 
}; 

template< class P > struct IsPtrImpl { 
    template< class X > static true_type f(X); 
    template< class X > static false_type f(...); 

    typedef decltype(f(*declval<P>())) type; 
}; 

template< class P > struct IsPtr : public IsPtrImpl<P>::type { }; 

template< class P, class R > struct EPtr : enable_if<IsPtr<P>::value,R> { }; 

template< class X > struct Mon< X* > 
{ 
    template< class F, class R = decltype(declval<F>()(declval<X>())) > 
    static unique_ptr<R> mon(X* x, F f) { 
     typedef unique_ptr<R> U; 
     return x ? U(new R(f(*x))) : U(nullptr); 
    } 
}; 

int add_one(int x) { return x + 1; } 

int main() 
{ 
    vector<int> v = {1,2,3,4,5};  
    int x = 5; 

    auto v2 = mon(v, add_one); 
    auto x2 = mon(&x, add_one); 

    // Should print 2 and 6. 
    cout << v2[0] << '\n'; 
    cout << *x2 << '\n'; 
} 

我希望做的是專門爲週一更通用的類型,但是當我再次嘗試使用enable_if繼承伎倆,GCC抱怨週一已定義。我也嘗試過使用第in this question號提到的第二個模板參數爲SFINAE的true_或false_type的技術,但沒有辦法讓它編譯。理想情況下,每當我想到一個類型的類型,我想定義一個動作,我應該能夠寫一個enable_if並寫在模板專業化的整個函數組。這節省了爲每個函數寫一個enable_if的麻煩。悲觀的是,我不得不爲每個類別中的每個似是而非的類型專門化組,以便真正成爲通用的。

我可以用通用和可擴展的方式寫這個嗎? PS:如果只有概念是C++的一部分11。

+0

您認爲在類型的基類列表中使用'std :: enable_if'會怎樣? (我問,因爲我想確保我明白你想達到什麼。) –

+0

你的意思是ESeq在Mon ?由於Mon的所有函數都是靜態的,所以基礎對象永遠不會被構建。它只是防止非序列類型的實例化。雖然,如果我也不能爲其他類型也是完全沒有用的,所以我寫了Mon 而不是Mon 。 – SplinterOfChaos

回答

3

注意:恐怕我沒有完全理解這個問題,所以我可能是脫節...

據我瞭解,你正在試圖解決一個編譯時間調度問題基本上,你想的結構是:

  • 泛型函數foo
  • 一個數字,對於一個給定類型的工作專門化(或類型的給定家庭)

而且你有問題既是說:

  • 你想要的foo_impl,只要你願意,並增加新的類型不應該影響現有的
  • 你需要選擇合適的foo_impl

聽起來不像簡單的功能嗎?

這是可以做到,相對簡單,使用的特徵。

void foo_tag_detection(...) {} // void cannot be passed down as an argument 
           // so if this is picked up we are guaranteed 
           // a compile-time error 

template <typename T> 
void foo(T const& t) { 
    foo_impl(t, foo_tag_detection(t)); 
} 

然後我們創建具體到我們的類型標籤,這裏序列

// For sequence we'll use a "sequence_tag", it can be reused for other functions 
struct sequence_tag {}; 

貫徹序列檢測,以及對foo_impl過載,我們需要

template <typename Seq> 
auto foo_tag_detection(Seq const& s) -> decltype(s.begin(), s.end(), sequence_tag{}) { 
    return sequence_tag{}; 
} 

template <typename Seq> 
void foo_impl(Seq const& s, sequence_tag) { ... } 

請注意,這裏使用decltype來觸發SFINAE,具體取決於我需要的操作s來支持;這是一個非常強大的機制,而且非常簡潔。

+0

這與我正在尋找的東西非常接近!唯一的區別是像Monoid這樣的一個類型可能定義了幾個函數,在這種情況下是mappend和mzero。但我打賭只需要多一點工作,我可以通過標籤專門化這些類型,而不是類型本身。所以我可能寫Monoid 和Monoid 。 順便說一句:我不知道你可以在decltype中使用逗號運算符! – SplinterOfChaos

+0

@SplinterOfChaos:我必須承認在'decltype'中逗號運算符**非常方便** :) –

+0

它的工作原理!非常感謝。令人驚訝的是有時候有多簡單的事情。 – SplinterOfChaos

2

通常,該工作的工具是模板專業化,只需SFINAE的幫助。以下是一些未經測試的代碼,可讓您體驗它的外觀:

/* Reusable utilities */ 

template<typename... T> 
struct dependent_false_type: std::false_type {}; 

enum class enabled; 

template<typename Cond> 
using EnableIf = typename std::enable_if<Cond::value, enabled>::type; 

template<typename T> 
using Bare = typename std::remove_cv< 
    typename std::remove_reference<T>::type 
>::type; 

/* Front-end */ 

// Second parameter is an implementation detail 
template<typename T, typename Sfinae = enabled> 
struct Mon { 
    // It's not allowed to use the primary template 
    static_assert(dependent_false_type<T>::value 
       , "No specialization of Mon found"); 

    // In case a compiler forgets to honour the assertion 
    template<typename... Ignored> 
    static void mon(Ignored const&...) = delete; 
}; 

// Front-end that delegates to the back-end 
template< 
    typename T 
    , typename F 
    , typename B = Bare<T> 
> 
auto mon(T&& t, F&& f) 
-> decltype(Mon<B>::template mon(std::declval<T>(), std::declval<F>())) 
{ return Mon<B>::template mon(std::forward<T>(t), std::forward<F>(f)); } 

/* Back-end for pointers */ 

template<typename T> 
struct Mon<T, EnableIf<std::is_pointer<T>>> { 
    // Implement somewhere 
    template< 
     typename P 
     , typename F 
     , typename R = decltype(std::declval<F>()(*std::declval<P>())) 
    > 
    static std::unique_ptr<R> mon(P&& p, F&& f); 
}; 

/* Back-end for ranges */ 

// Boost.Range does provide range concepts but not range traits 
// so let's roll our own crude is_range 

namespace detail { 

// actual range concepts of Boost.Range also require some member types 
// left as an exercise to the reader 
template<typename T> 
is_range_tester { 
    template< 
     typename T 
     , typename = decltype(boost::begin(std::declval<T>())) 
     , typename = decltype(boost::end(std::declval<T>())) 
    > 
    static std::true_type test(int); 

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

} // detail 

template<typename T> 
struct is_range 
    : decltype(detail::is_range_tester<T&>::template test<T>(0)) 
{}; 

template<typename T> 
struct Mon<T, EnableIf<is_range<T>>> { 
    /* implementation left as an exercise */ 
}; 

這也是可擴展的。最大的障礙是每個專業化的條件不能重疊。

相關問題