1

休眠コードはgcc4.7で動作します。アイデアは、シーケンス、ポインター、タプル、ペア、ユーザー定義型などで機能するこれらの総称関数を持っているということです。これらの関数の1つが型に対して定義されている場合、すべてが定義されている必要があります。私が抱えている問題は、それらをどのように専門化するかを決定することです。タイプごとに特化したテンプレートクラスを定義し、各関数を実装してから、クラス内の実装に転送するだけの無料の関数を定義することにしました。

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

私がやりたいのは、より一般的な型にMonを特化することですが、enable_if継承トリックを再度使用しようとすると、gccはMonがすでに定義されていると文句を言います。この質問で述べたように、2番目のテンプレート引数をSFINAEのtrue_またはfalse_typeにする手法も試しましたが、コンパイルすることができませんでした。

理想的には、アクションを定義したいタイプのカテゴリを考えるときはいつでも、enable_ifを記述して、関数のグループ全体をテンプレートの特殊化に記述できる必要があります。これにより、関数ごとに1つのenable_ifを作成する手間が省けます。悲観的に、私は本当に一般的であるために、各カテゴリーのすべてのもっともらしいタイプのためにグループを専門化する必要があるでしょう。

これを一般的で拡張可能な方法で書くことはできますか?

PS:コンセプトだけがC++11の一部だったとしたら。

4

2 に答える 2

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それは本当に強力なメカニズムであり、驚くほど簡潔です。

于 2012-08-25T15:24:53.153 に答える
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 */
};

これも拡張可能です。最大の障害は、各専門分野の条件が重複してはならないことです。

于 2012-08-25T16:51:10.567 に答える