7

編集:私の質問に対する簡単な答えは、SFINAE ができることについて間違った見方をしており、関数本体をまったくチェックしていないということです: sfinae は関数本体をインスタンス化しますか?

これに似た問題があります:関数の存在を確認するテンプレートを作成することは可能ですか?

違いは、関数が存在するかどうかを確認するだけでなく、実際に SFINAE を渡すかどうかも知りたいということです。これが私が達成しようとしているものの例です:

struct A
{
    void FuncA() { std::cout << "A::FuncA" << std::endl; }
};

struct B
{
    void FuncA() { std::cout << "B::FuncA" << std::endl; }
    void FuncB() { std::cout << "B::FuncB" << std::endl; }
};

template<typename T>
struct Inter
{
    void FuncA() { t.FuncA(); }
    void FuncB() { t.FuncB(); }

    T t;
};

// Always takes some sort of Inter<T>.
template<typename InterType>
struct Final
{
    void CallFuncs()
    {
        // if( t.FuncA() exists and can be called )
            t.FuncA();

        // if( t.FuncB() exists and can be called )
            t.FuncB();
    }

    InterType t;
};

void DoEverything()
{
    Final<Inter<A>> finalA;
    Final<Inter<B>> finalB;

    finalA.CallFuncs();
    finalB.CallFuncs();
}

CallFuncs() では、FuncA() と FuncB() の両方が常に存在しますが、Inter で使用される型 T によってはコンパイルされない場合があることに注意してください。上記のリンクされた質問の回答を使用しようとすると、実際にコンパイルできることではなく、関数が存在することを確認するだけであるため、常に真になるように思われます(ただし、それを除外することはできません)何かを台無しにしたわけではありません...)

関数を条件付きで呼び出すために、 enable_if をそのまま使用できると思います。

template<typename InterType>
typename std::enable_if< ! /* how to determine if FuncA can be called? */>::type TryCallFuncA( InterType& i )
{
}
template<typename InterType>
typename std::enable_if</* how to determine if FuncA can be called? */>::type TryCallFuncA( InterType& i )
{
    i.FuncA();
}

template<typename InterType>
typename std::enable_if< ! /* how to determine if FuncB can be called? */>::type TryCallFuncB( InterType& i )
{
}
template<typename InterType>
typename std::enable_if</* how to determine if FuncB can be called? */>::type TryCallFuncB( InterType& i )
{
    i.FuncB();
}

template<typename InterType>
struct Final
{
    void CallFuncs()
    {
        TryCallFuncA(t);
        TryCallFuncB(t);
    }

    InterType t;
};

しかし、ブール値をenable_ifに渡す方法があるかどうかはわかりません。これを達成する方法はありますか、または関数が存在するかどうかを示す何らかの手動で維持される型特性にフォールバックする必要がありますか?

利用可能な C++11 機能セットに関する限り、私は MSVC 2010 を使用しています。

編集: 重要なメモを追加するには、私の実際の状況では、クラス Inter の実装は、Inter::FuncA/FuncB がコンパイルされるかどうかを判断する必要がある時点で事実上不透明であるため、子をバブルアップすることはできません型を調べ、関数が存在するかどうかを確認します。

4

1 に答える 1

8

今はこれを確認する時間はありませんが、 : の特殊化を追加できますFinal(template <typename T> struct Final< Inner<T> >;これは、型が常に a であることを保証するのにも役立ちますInner。これにより、インスタンス化に使用される型を抽出できますInter

2 番目の問題は、メンバー関数が存在するかどうかを検出するために SFINAE を使用する方法です。これはあまり複雑であってはならないと思います (これを汎用にする必要がない場合):

// Find out whether U has `void f()` member
template <typename U>
struct has_member_f {
    typedef char yes;
    struct no { char _[2]; };
    template<typename T, void (T::*)() = &T::f>
    static yes impl( T* );
    static no  impl(...);

    enum { value = sizeof( impl( static_cast<U*>(0) ) ) == sizeof(yes) };
};

これを少し拡張してもう少し一般的なものにすることもできるかもしれませんが、関数の名前を一般的にすることはできないと思います。もちろん、それを生成has_member_##argして使用するマクロとして書くこともできます&T:: arg。メンバーのタイプは、おそらく一般化する方が簡単です...

別の方法として、これを汎用にできるとは思わないのでhas_member、型の内部で直接このトリックを使用できます: 2 つのcallFuncAオーバーロードを提供します。1 つはオプションの 2 番目の引数でテンプレート化され、必要なシグネチャを持ち&T::FuncA、呼び出しを転送するようにデフォルト設定されています。もう 1 つは noop である省略記号です。次にandcallFuncsを呼び出すcallFuncAcallFuncB、SFINAE がフォワーダーまたは正午にディスパッチされ、目的の動作が得られます。

template<typename T>
struct Final< Inter<T> >
{
    template <typename U, void (U::*)() = &U::FuncA>
    void callFuncA( Inter<T>* x ) {
        x.FuncA();
    }
    void callFuncA(...) {}

    void CallFuncs() {
        callFuncA(&t);                 // Cannot pass nonPOD types through ...
        // Similarly TryCallFuncB(t);
    }
    Inter<T> t;
};
于 2012-06-09T03:31:41.960 に答える