1. ホーム
  2. c++

クラス内に特定のメンバ変数が存在するかどうかを検出する方法は?

2023-11-11 08:20:23

質問

アルゴリズムのテンプレート関数を作成するために、私はテンプレートの引数であるクラスのxまたはX(およびyまたはY)かどうかを知る必要があります。これは、MFC CPointクラスまたはGDI+ PointFクラスまたはいくつかの他のために私の関数を使用するときに有用であるかもしれません。これらのすべては、それらの中で異なるxを使用しています。私の解決策は、次のコードに減らすことができます。


template<int> struct TT {typedef int type;};
template<class P> bool Check_x(P p, typename TT<sizeof(&P::x)>::type b = 0) { return true; }
template<class P> bool Check_x(P p, typename TT<sizeof(&P::X)>::type b = 0) { return false; }

struct P1 {int x; };
struct P2 {float X; };
// it also could be struct P3 {unknown_type X; };

int main()
{
    P1 p1 = {1};
    P2 p2 = {1};

    Check_x(p1); // must return true
    Check_x(p2); // must return false

    return 0;
}

しかし、Visual Studioではコンパイルできず、GNU C++でコンパイルしています。Visual Studioでは以下のようなテンプレートが使えました。


template<class P> bool Check_x(P p, typename TT<&P::x==&P::x>::type b = 0) { return true; }
template<class P> bool Check_x(P p, typename TT<&P::X==&P::X>::type b = 0) { return false; }

しかし、GNU C++ではコンパイルできません。普遍的な解決策はあるのでしょうか?

UPD: ここでの構造体P1,P2はあくまで例です。未知のメンバを持つクラスが存在する可能性があります。

P.S. C++11の解答をここに投稿しないでください。

どのように解決するのですか?

もう一つの方法は、このようなものです。 SFINAE を使って表現する方法です。 にも依存しています。名前のルックアップの結果が曖昧な場合、コンパイラはそのテンプレートを拒否します。

template<typename T> struct HasX { 
    struct Fallback { int x; }; // introduce member name "x"
    struct Derived : T, Fallback { };

    template<typename C, C> struct ChT; 

    template<typename C> static char (&f(ChT<int Fallback::*, &C::x>*))[1]; 
    template<typename C> static char (&f(...))[2]; 

    static bool const value = sizeof(f<Derived>(0)) == 2;
}; 

struct A { int x; };
struct B { int X; };

int main() { 
    std::cout << HasX<A>::value << std::endl; // 1
    std::cout << HasX<B>::value << std::endl; // 0
}

これはusenet上の誰かの素晴らしいアイディアに基づくものです。

注意: HasX は、任意の型を持つ x という名前のデータまたは関数メンバをチェックします。メンバー名を導入する唯一の目的は、メンバー名の検索に曖昧さを持たせることであり、メンバーの型は重要ではありません。