5

重複の可能性:
C++ テンプレートを記述して関数の存在を確認することは可能ですか?

2つのクラスがあるとします:

struct A{ int GetInt(){ return 10; } };
struct B{ int m; };

次の関数でタイプ A または B のオブジェクトを使用したい

tempate< typename T >
int GetInt( const T & t )
{
   //if it's A, I'll call: return t.GetInt();
   //if its' B, I'll call: return t.m;
}

さて、たくさんのクラスがあり、GetInt() を含むものも含まないものもあります。型ごとに特殊化を記述したくありません。' GetInt() を含むか、コンパイル時に含まないかで区別したいだけです。 '、どうすればいいですか?

4

4 に答える 4

6

置換の失敗はエラーではありません。よりコンパクトに言えば、SFINAEです。

ただし、特定のケースでは、SFINAE、仮想メンバー、またはそのような派手なものは必要ありません。

通常のオーバーロードされた関数が必要です。

int GetInt(A& t) { return t.GetInt(); }
int GetInt(const B& t) { return t.m; }

異なるバージョン間で共有する必要のあるコードがある場合は、オーバーロードされたインライン関数を呼び出すテンプレートがあり、すべてのタイプ固有の動作がインライン関数にあり、すべての共有動作がテンプレートにあるように、コードをリファクタリングします。

「私には多くのクラスがあります」というニーズに対して、SFINAEは多かれ少なかれ次のようになります。

template<typename T>
int GetInt(const T& t, int (T::*extra)() const = &T::GetInt)
{
    return t.GetInt();
}

template<typename T>
auto GetInt(const T& t) -> decltype(t.m)
{
    return t.m;
}

編集:SFINAEの現実は、少なくともC ++ 0xが登場するまでは、はるかに醜いものです。実際、それはGManの答えと同じくらい悪く見え始めます。

struct A{ int GetInt() const { return 10; } };
struct B{ int m; };

template<typename T, int (T::*extra)() const>
struct has_mfunc
{
    typedef int type;
};

template<typename T>
typename has_mfunc<T, &T::GetInt>::type GetInt(const T& t)
{
    return t.GetInt();
}

template<typename T, typename U, U (T::*extra)>
struct has_field
{
    typedef U type;
};

template<typename T>
typename has_field<T, int, &T::m>::type GetInt(const T& t)
{
    return t.m;
}

int main(void)
{
   A a;
   B b;
   b.m = 5;
   return GetInt(a) + GetInt(b);
}
于 2010-10-19T00:05:00.043 に答える
4

ここから盗み、コードを修正すると仮定すると、GetIntconstは次のようになります。

HAS_MEM_FUNC(GetInt, has_GetInt);

template <bool B>
struct bool_type
{
    static const bool value = B;
};

typedef bool_type<true> true_type;
typedef bool_type<false> false_type;

namespace detail
{
    template <typename T>
    int get_int(const T& pX, true_type)
    {
        return pX.GetInt();
    }

    template <typename T>
    int get_int(const T& pX, false_type)
    {
        return pX.m;
    }
}

template <typename T>
int get_int(const T& pX)
{
    return detail::get_int(pX,
                            has_GetInt<T, int (T::*)() const>::value);
}

しかし、これはかなりひどいデザインです。パッチを適用するのではなく、問題を修正する必要があります。

于 2010-10-19T00:01:05.573 に答える
0

技術的には、グーグルなどで見つけることができるいくつかのテンプレート アルカナが含まれますhas_member。袖口から、検出コードで、そのようなものを書くとしたら、問題のクラスから偽の派生を行い、派生クラスのメンバーのサイズを確認するだけです。

ただし、そうしないでください。

他に何をすべきかによって異なります。しかし、あなたのクラスは、いわば2つの異なる「スキーマ」に準拠しているように見えます.タイプシステムを介してそれらのスキーマを利用することはできません(クラスは2つの基本クラスAとBから派生していないようです)。次に、1 つのオプションは、テンプレート パラメーター T がスキーマ A か B かをラッパーに伝える traits テンプレートを導入することです。デフォルトとは異なる関連する各クラスの特性を特殊化します。作業を最小限に抑えるために、デフォルトを選択します。

乾杯 & hth.,

于 2010-10-18T23:58:59.243 に答える
0

これがまさに継承の目的です。実行時にこのタイプの質問に dynamic_cast を簡単に使用できます。たとえば、HasGetInt という抽象基本クラスを定義し、そこからその関数を必要とするクラスを派生させることができます。車輪を再発明する必要はありません。

于 2010-10-18T23:58:59.697 に答える