2

次のタグディスパッチコードがあります(LiveWorkSpaceを参照)

#include <iostream>

// traits types
struct A {}; struct B {}; struct C {};

// helpers
void fun_impl(bool, A) { std::cout << "A\n"; }
void fun_impl(bool, B) { std::cout << "B\n"; }

// would like to eliminate this
void fun_impl(bool b, C) 
{ 
  if(b) 
    fun_impl(b, A()); 
  else 
    fun_impl(b, B()); 
}

template<typename T>
void fun(bool b, T t)
{
    // T = A, B, or C
    fun_impl(b, t);        
}

int main()
{
    fun(true,  A()); // "A"
    fun(false, A()); // "A"
    fun(true,  B()); // "B"
    fun(false, B()); // "B"
    fun(true,  C()); // "A"
    fun(false, C()); // "B"
}

ただし、このタグのディスパッチは関数と密接に結びついており、funこれを使用するすべての関数に対してこのタグのディスパッチを実装するには、3 つのヘルパー関数を維持する必要があります。

引数推定失敗:関数オブジェクトfun_implのテンプレート パラメーターに抽象化しようとしましたが、引数として渡すと、2 つのオーバーロードのどちらが必要かを推定できません。mixed_dispatchfun_impl

template<typename T>
struct mixed_dispatch
{
    template<typename Fun>
    void operator()(Fun f, bool b)
    {
        return f(b, T());
    }
};

template<>
struct mixed_dispatch<C>
{
    template<typename Fun>
    void operator()(Fun f, bool b)
    {
        if (b) 
           return f(b, A());
        else
           return f(b, B());
    }
};

template<typename T>
void fun(bool b, T)
{
    // T = A, B, or C
    mixed_dispatch<T>()(fun_impl, b); // ERROR: Fun cannot be deduced   
}

質問: 呼び出される関数からタグ ディスパッチを分離する他の方法はありますか?

C++11 可変個引数テンプレート/Boost.Fusion または現在のコードを簡素化するその他のウィザードリィを使用する提案を受け入れます (この特定のディスパッチを使用するすべての関数に対して、2 つの代わりに 3 つのヘルパー関数を維持する必要があります。複雑なディスパッチにより、ヘルパー関数の数がさらに速くなります)。

4

2 に答える 2

1

オーバーロードされた関数の 1 つを選択するには、少なくともターゲット関数の引数の型をコンパイラに伝える必要があります。したがって、それらをテンプレート クラスの型引数として追加できます。mixed_dispatch

template < typename Tag, typename... Args >
class mixed_dispatch {
    std::function<void(Tag,Args...)> invoke;

public:
    // for overloaded functions
    mixed_dispatch( void(&f)(Tag,Args...) ) : invoke( f ) { }

    // for function objects
    template < typename F >
    mixed_dispatch( F&& f ) : invoke( std::forward<F>(f) ) { }

    void operator()( Args... args ) const {
        invoke( Tag(), args... );
    }
};

オブジェクトをターゲット関数にmixed_dispatch渡すのに役立つラッパーになりました。Tagご覧のとおり、ターゲット関数のシグネチャを変更する必要があります (少しは機能します)。

void fun_impl(A1, bool) { std::cout << "A1\n"; }

クライアント コードでは、次のようになりfunます。

template< typename T >
void fun( bool b, T )
{
    using dispatcher = mixed_dispatch<T,bool>;
    dispatcher d = fun_impl;
    d( b );
}
于 2013-02-02T15:30:33.017 に答える
0

それはあなたの質問に答えますか?

// traits types
struct A1 {}; struct B1 {};
struct A2 {}; struct B2 {};

template<class T1, class T2>
struct TypeSelector
{
    typedef T1 TTrue;
    typedef T2 TFalse;
};

typedef TypeSelector<A1, B1> C1;
typedef TypeSelector<A2, B2> C2;

// helpers
void fun_impl(bool, A1) { std::cout << "A1\n"; }
void fun_impl(bool, B1) { std::cout << "B1\n"; }
void fun_impl(bool, A2) { std::cout << "A2\n"; }
void fun_impl(bool, B2) { std::cout << "B2\n"; }

template<class TSel>
void fun_impl(bool b, TSel) { if(b) fun_impl(b, typename TSel::TTrue()); else fun_impl(b, typename TSel::TFalse()); }

template<typename T>
void fun(bool b, T t)
{
    // T = A, B, or C
    fun_impl(b, t);
}

int main()
{
    fun(true,  A1()); // "A1"
    fun(false, A1()); // "A1"
    fun(true,  B1()); // "B1"
    fun(false, B1()); // "B1"
    fun(true,  C1()); // "A1"
    fun(false, C1()); // "B1"
    fun(true,  C2()); // "A2"
    fun(false, C2()); // "B2"
}

それとも、他の「次元」でコードを簡素化したいですか?

于 2013-02-02T11:40:49.447 に答える