5

この質問への回答を読んだ後、クラスに特定のメンバー関数があるかどうかに基づいて、SFINAEを使用して2つの関数から選択できることを学びました。これは次と同等ですが、ifステートメントの各ブランチがオーバーロードされた関数に分割されているだけです。

template<typename T>
void Func(T& arg)
{
    if(HAS_MEMBER_FUNCTION_X(T))
        arg.X();
    else
        //Do something else because T doesn't have X()
}

になります

template<typename T>
void Func(T &arg, int_to_type<true>); //T has X()

template<typename T>
void Func(T &arg, int_to_type<false>); //T does not have X()

SFINAEを拡張して複数のルールを実行できるかどうか疑問に思いました。これに相当するもの:

template<typename T>
void Func(T& arg)
{
    if(HAS_MEMBER_FUNCTION_X(T))                //See if T has a member function X  
        arg.X();
    else if(POINTER_DERIVED_FROM_CLASS_A(T))    //See if T is a pointer to a class derived from class A
        arg->A_Function();              
    else if(DERIVED_FROM_CLASS_B(T))            //See if T derives from class B
        arg.B_Function();
    else if(IS_TEMPLATE_CLASS_C(T))             //See if T is class C<U> where U could be anything
        arg.C_Function();
    else if(IS_POD(T))                          //See if T is a POD type
        //Do something with a POD type
    else
        //Do something else because none of the above rules apply
}

このようなことは可能ですか?

ありがとうございました。

4

3 に答える 3

6

これは確かに可能です。すべてのブランチが相互に排他的であることを確認するように注意する必要があります。そうしないと、あいまいになってしまいます。

これをサポートするための2つの最良のツールである BoostTypeTraitsBoostEnableIfを見てください。Boost ICE(Integral Constant Expressionの略)を使用して、複数の型特性を組み合わせて、より複雑な型マッチングを実行できるようにします(また、オーバーロードが相互に排他的であることを確認します。

これはやや複雑で複雑になる可能性があるため、ここでは比較的簡単な例を示します。クラス階層があるとします。

struct Base { };
struct Derived : Base { };

そして、関数の1つのオーバーロードを呼び出しfooBase別のオーバーロードを。から派生したクラスに対して呼び出しますBase。最初の試みは次のようになります。

#include <boost/type_traits.hpp>
#include <boost/utility/enable_if.hpp>

using namespace boost;
using namespace boost::type_traits;

template <typename T>
typename enable_if<is_same<Base, T>, void>::type
foo(const T&) { }

template <typename T>
typename enable_if<is_base_of<Base, T>, void>::type
foo(const T&) { } 

ただし、が基本クラスis_base_ofの場合はtrueを返しますT。したがって、を呼び出そうとするとfoo(Base())、両方の関数テンプレートが一致するため、あいまいさが生じます。タイプ特性の組み合わせとBoostICEヘルパーを使用することで、これを解決できます。

template <typename T>
typename enable_if<is_same<Base, T>, void>::type
foo(const T&) { }

template <typename T>
typename enable_if<
    ice_and<
        is_base_of<Base, T>::value,
        ice_not<is_same<Base, T>::value>::value 
    >, void>::type
foo(const T&) { }

これらのオーバーロードは相互に排他的であり、あいまいさがないことを保証します。

いくつかの例はサポートされていません(つまり、HAS_MEMBER_FUNCTION_X;私にはわかりませIS_TEMPLATE_CLASS_Cん-それを使って何をしたいかによっては、何かを機能させることができるかもしれません)が、一般的には可能です。

于 2010-04-27T02:44:17.710 に答える
1

あなたがそれを理解するとき、質問は簡単です

if (a) { X(); }
else if (b) { Y(); }

とまったく同じ意味

if (a) { X(); }
if (!a && b) { Y(); }

ただし、二分法を拡張することもできますtrue/false

enum FuncVariants { HasMember, PointerDerivedFromA, DerivedFromB, InstanceOfC, isPod }
template<typename T>
void Func(T &arg, int_to_type<HasMember>);

template<typename T>
void Func(T &arg, int_to_type<DerivedFromA>);

template<typename T>
void Func(T &arg, int_to_type<DerivedFromB>);

template<typename T>
void Func(T &arg, int_to_type<InstanceOfC>);

(明らかに、オプションは相互に排他的ではないため、電話をかけるときは注意が必要です)

于 2010-04-27T09:34:46.040 に答える
1

あなたがそれを実装した方法ではありません。argに関数がない場合、コンパイルは失敗します。(私はあなたがこれを知っていると思います、ただ確認してください)。

ただし、テンプレートの特殊化(ブーストmplの魔法に隠されている)を使用してこれを行うことは可能です。

メタ関数を備えたブーストmplベクトルを使用してこれを行うことができます:http://www.boost.org/doc/libs/1_40_0/libs/mpl/doc/refmanual.htmlをチェックして ください

typedefs typename mpl::vector<f0,f1,...>::type handlers; // different handlers
// convert logic to int N to map condition to handler
// can use ternary or bit shift trick
// more general approach could be to use vector of mpl::bool_ and mpl::find

typedef typename mpl::vector_c<bool, (first_condition),
                                     (second_condition),...>::type condition;

typedef typename mpl::find<condition, mpl:: bool_<true> >::type iterator;
typedef typename mpl::at<handlers, iterator::pos::value>::type handler;
handler::apply(...); // call handler with some arguments

正確な要件に応じて、さまざまなアプローチを試すことができます。上記は数時間前に行われたことです

于 2010-04-27T02:45:33.087 に答える