クラスに特定のメンバー変数があるかどうかを検出する方法は?
-
05-07-2019 - |
質問
アルゴリズムテンプレート関数を作成するには、テンプレート引数であるクラスの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;
}
ただし、GNU C ++でコンパイルしている間はVisual Studioでコンパイルしません。 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は、例にすぎません。メンバーが不明なクラスが存在する可能性があります。
PS 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をチェックします。メンバー名を紹介する唯一の目的は、メンバー名の検索にあいまいさを持たせることです-メンバーのタイプは重要ではありません。
他のヒント
これはよりも簡単なソリューションです。 Johannes Schaub-litb の one 。 C ++ 11が必要です。
#include <type_traits>
template <typename T, typename = int>
struct HasX : std::false_type { };
template <typename T>
struct HasX <T, decltype((void) T::x, 0)> : std::true_type { };
更新:簡単な例と、この仕組みの説明。
これらのタイプの場合:
struct A { int x; };
struct B { int y; };
HasX&lt; A&gt; :: value == true
および HasX&lt; B&gt; :: value == false
があります。理由を見てみましょう。
まず、 std :: false_type
および std :: true_type
には value
static constexpr bool メンバーがあることを思い出してください>それぞれ false
および true
に設定されます。したがって、上記の2つのテンプレート HasX
はこのメンバーを継承します。 ( std :: false_type
の最初のテンプレートと std :: true_type
の2番目のテンプレート。)
簡単に始めてから、上記のコードに到達するまで段階的に進めましょう。
1)開始点:
template <typename T, typename U>
struct HasX : std::false_type { };
この場合、驚きはありません: HasX
は std :: false_type
から派生しているため、 HasX&lt; bool、double&gt; :: value == false
および HasX&lt; bool、int&gt; :: value == false
。
2)デフォルトの U
:
// Primary template
template <typename T, typename U = int>
struct HasX : std::false_type { };
U
のデフォルトは int
であるとすると、 Has&lt; bool&gt;
は実際には HasX&lt; bool、int&gt;
を意味しますしたがって、 HasX&lt; bool&gt; :: value == HasX&lt; bool、int&gt; :: value == false
。
3)専門分野の追加:
// Primary template
template <typename T, typename U = int>
struct HasX : std::false_type { };
// Specialization for U = int
template <typename T>
struct HasX<T, int> : std::true_type { };
一般に、プライマリテンプレートのおかげで、 HasX&lt; T、U&gt;
は std :: false_type
から派生しています。ただし、 std :: true_type
から派生した U = int
の特殊化が存在します。したがって、 HasX&lt; bool、double&gt; :: value == false
ですが、 HasX&lt; bool、int&gt; :: value == true
です。
U
のデフォルトのおかげで、 HasX&lt; bool&gt; :: value == HasX&lt; bool、int&gt; :: value == true
。
4) decltype
および int
の言い回し:
少々余談ですが、どうか我慢してください。
基本的に(これは完全に正しいわけではありません)、 decltype(expression)
は expression の型を生成します。たとえば、 0
のタイプは int
であるため、 decltype(0)
は int
を意味します。同様に、 1.2
のタイプは double
であるため、 decltype(1.2)
は double
を意味します。
次の宣言を使用して関数を検討してください:
char func(foo, int);
where foo
は何らかのクラスタイプです。 f
が foo
型のオブジェクトである場合、 decltype(func(f、0))
は char
( func(f、0)
)によって返される型。
現在、式(1.2、0)
は、2つの部分式を順番に評価する(組み込みの)コンマ演算子を使用します(つまり、最初の 1.2
0
)、最初の値を破棄し、2番目の値になります。したがって、
int x = (1.2, 0);
は同等です
int x = 0;
これを decltype
と合わせると、 decltype(1.2、0)
は int
を意味します。ここでは 1.2
または double
について特別なことは何もありません。たとえば、 true
のタイプは bool
で、 decltype(true、0)
は int
も意味します。
クラスタイプはどうですか?インスタンスでは、 decltype(f、0)
はどういう意味ですか?これが int
を意味することを期待するのは自然ですが、そうではないかもしれません。実際、上記の関数 func
と同様に、 foo
と int
を取り、 char
。この場合、 decltype(foo、0)
は char
です。
どのようにしてtのオーバーロードの使用を避けることができますか
質問からリダイレクトされましたこれの複製として閉じられました。古いスレッドであることは知っていますが、C ++ 11で動作する代替(単純な)実装を提案したかっただけです。特定のクラスに id
というメンバー変数があるかどうかを確認するとします:
#include <type_traits>
template<typename T, typename = void>
struct has_id : std::false_type { };
template<typename T>
struct has_id<T, decltype(std::declval<T>().id, void())> : std::true_type { };
それだけです。そして、これがどのように使用されるかです( 実際の例 ) :
#include <iostream>
using namespace std;
struct X { int id; };
struct Y { int foo; };
int main()
{
cout << boolalpha;
cout << has_id<X>::value << endl;
cout << has_id<Y>::value << endl;
}
いくつかのマクロを使用して、物事をさらに簡単にすることができます:
#define DEFINE_MEMBER_CHECKER(member) \
template<typename T, typename V = bool> \
struct has_ ## member : false_type { }; \
template<typename T> \
struct has_ ## member<T, \
typename enable_if< \
!is_same<decltype(declval<T>().member), void>::value, \
bool \
>::type \
> : true_type { };
#define HAS_MEMBER(C, member) \
has_ ## member<C>::value
次のように使用できます:
using namespace std;
struct X { int id; };
struct Y { int foo; };
DEFINE_MEMBER_CHECKER(foo)
int main()
{
cout << boolalpha;
cout << HAS_MEMBER(X, foo) << endl;
cout << HAS_MEMBER(Y, foo) << endl;
}
更新:最近、元の回答に投稿したコードをさらに使用したため、変更/追加を考慮してこれを更新しています。
使用方法のスニペットは次のとおりです。 *これらすべての根性はさらに下にあります
指定されたクラスのメンバー x
を確認します。 var、func、class、union、enumのいずれかです:
CREATE_MEMBER_CHECK(x);
bool has_x = has_member_x<class_to_check_for_x>::value;
メンバー関数の確認 void x()
:
//Func signature MUST have T as template variable here... simpler this way :\
CREATE_MEMBER_FUNC_SIG_CHECK(x, void (T::*)(), void__x);
bool has_func_sig_void__x = has_member_func_void__x<class_to_check_for_x>::value;
メンバー変数 x
を確認します:
CREATE_MEMBER_VAR_CHECK(x);
bool has_var_x = has_member_var_x<class_to_check_for_x>::value;
メンバークラス x
を確認します:
CREATE_MEMBER_CLASS_CHECK(x);
bool has_class_x = has_member_class_x<class_to_check_for_x>::value;
メンバーユニオン x
を確認:
CREATE_MEMBER_UNION_CHECK(x);
bool has_union_x = has_member_union_x<class_to_check_for_x>::value;
メンバー列挙 x
を確認:
CREATE_MEMBER_ENUM_CHECK(x);
bool has_enum_x = has_member_enum_x<class_to_check_for_x>::value;
署名に関係なく、メンバー関数 x
を確認します:
CREATE_MEMBER_CHECK(x);
CREATE_MEMBER_VAR_CHECK(x);
CREATE_MEMBER_CLASS_CHECK(x);
CREATE_MEMBER_UNION_CHECK(x);
CREATE_MEMBER_ENUM_CHECK(x);
CREATE_MEMBER_FUNC_CHECK(x);
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;
または
CREATE_MEMBER_CHECKS(x); //Just stamps out the same macro calls as above.
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;
詳細とコア:
/*
- Multiple inheritance forces ambiguity of member names.
- SFINAE is used to make aliases to member names.
- Expression SFINAE is used in just one generic has_member that can accept
any alias we pass it.
*/
template <typename... Args> struct ambiguate : public Args... {};
template<typename A, typename = void>
struct got_type : std::false_type {};
template<typename A>
struct got_type<A> : std::true_type {
typedef A type;
};
template<typename T, T>
struct sig_check : std::true_type {};
template<typename Alias, typename AmbiguitySeed>
struct has_member {
template<typename C> static char ((&f(decltype(&C::value))))[1];
template<typename C> static char ((&f(...)))[2];
//Make sure the member name is consistently spelled the same.
static_assert(
(sizeof(f<AmbiguitySeed>(0)) == 1)
, "Member name specified in AmbiguitySeed is different from member name specified in Alias, or wrong Alias/AmbiguitySeed has been specified."
);
static bool const value = sizeof(f<Alias>(0)) == 2;
};
マクロ(エルディアブロ!):
CREATE_MEMBER_CHECK:
//Check for any member with given name, whether var, func, class, union, enum.
#define CREATE_MEMBER_CHECK(member) \
\
template<typename T, typename = std::true_type> \
struct Alias_##member; \
\
template<typename T> \
struct Alias_##member < \
T, std::integral_constant<bool, got_type<decltype(&T::member)>::value> \
> { static const decltype(&T::member) value; }; \
\
struct AmbiguitySeed_##member { char member; }; \
\
template<typename T> \
struct has_member_##member { \
static const bool value \
= has_member< \
Alias_##member<ambiguate<T, AmbiguitySeed_##member>> \
, Alias_##member<AmbiguitySeed_##member> \
>::value \
; \
}
CREATE_MEMBER_VAR_CHECK:
//Check for member variable with given name.
#define CREATE_MEMBER_VAR_CHECK(var_name) \
\
template<typename T, typename = std::true_type> \
struct has_member_var_##var_name : std::false_type {}; \
\
template<typename T> \
struct has_member_var_##var_name< \
T \
, std::integral_constant< \
bool \
, !std::is_member_function_pointer<decltype(&T::var_name)>::value \
> \
> : std::true_type {}
CREATE_MEMBER_FUNC_SIG_CHECK:
//Check for member function with given name AND signature.
#define CREATE_MEMBER_FUNC_SIG_CHECK(func_name, func_sig, templ_postfix) \
\
template<typename T, typename = std::true_type> \
struct has_member_func_##templ_postfix : std::false_type {}; \
\
template<typename T> \
struct has_member_func_##templ_postfix< \
T, std::integral_constant< \
bool \
, sig_check<func_sig, &T::func_name>::value \
> \
> : std::true_type {}
CREATE_MEMBER_CLASS_CHECK:
//Check for member class with given name.
#define CREATE_MEMBER_CLASS_CHECK(class_name) \
\
template<typename T, typename = std::true_type> \
struct has_member_class_##class_name : std::false_type {}; \
\
template<typename T> \
struct has_member_class_##class_name< \
T \
, std::integral_constant< \
bool \
, std::is_class< \
typename got_type<typename T::class_name>::type \
>::value \
> \
> : std::true_type {}
CREATE_MEMBER_UNION_CHECK:
//Check for member union with given name.
#define CREATE_MEMBER_UNION_CHECK(union_name) \
\
template<typename T, typename = std::true_type> \
struct has_member_union_##union_name : std::false_type {}; \
\
template<typename T> \
struct has_member_union_##union_name< \
T \
, std::integral_constant< \
bool \
, std::is_union< \
typename got_type<typename T::union_name>::type \
>::value \
> \
> : std::true_type {}
CREATE_MEMBER_ENUM_CHECK:
//Check for member enum with given name.
#define CREATE_MEMBER_ENUM_CHECK(enum_name) \
\
template<typename T, typename = std::true_type> \
struct has_member_enum_##enum_name : std::false_type {}; \
\
template<typename T> \
struct has_member_enum_##enum_name< \
T \
, std::integral_constant< \
bool \
, std::is_enum< \
typename got_type<typename T::enum_name>::type \
>::value \
> \
> : std::true_type {}
CREATE_MEMBER_FUNC_CHECK:
//Check for function with given name, any signature.
#define CREATE_MEMBER_FUNC_CHECK(func) \
template<typename T> \
struct has_member_func_##func { \
static const bool value \
= has_member_##func<T>::value \
&& !has_member_var_##func<T>::value \
&& !has_member_class_##func<T>::value \
&& !has_member_union_##func<T>::value \
&& !has_member_enum_##func<T>::value \
; \
}
CREATE_MEMBER_CHECKS:
//Create all the checks for one member. Does NOT include func sig checks.
#define CREATE_MEMBER_CHECKS(member) \
CREATE_MEMBER_CHECK(member); \
CREATE_MEMBER_VAR_CHECK(member); \
CREATE_MEMBER_CLASS_CHECK(member); \
CREATE_MEMBER_UNION_CHECK(member); \
CREATE_MEMBER_ENUM_CHECK(member); \
CREATE_MEMBER_FUNC_CHECK(member)
Boost.ConceptTraits は他のマクロとたとえば、 BOOST_TT_EXT_DEFINE_HAS_MEMBER(name)
のように、型特性を定義します。これは、次の形式の型特性を定義します。
has_member_##name<T>
Tにという名前のメンバータイプがある場合、これはtrueになります。ただし、これは参照型のメンバーを検出しないことに注意してください。
あなたの場合、ヘッダーファイルに追加するだけで十分です
BOOST_TT_EXT_DEFINE_HAS_MEMBER_TYPE(x)
次のように確認します
BOOST_STATIC_ASSERT(has_member_x<P1>::value);
使用する手法は、前述の回答のいくつかで説明した手法と同じです。
残念ながら、このライブラリはメンテナンスされていません。 C ++ 0xには概念が含まれていないため、このライブラリとSFINAEを組み合わせることで、ほとんどの概念を処理できます。
次のような専門化を使用しないのはなぜですか:
struct P1 {int x; };
struct P2 {int X; };
template<class P>
bool Check_x(P p) { return true; }
template<>
bool Check_x<P2>(P2 p) { return false; }
これに対する2番目の回答(litb's)は、メンバーを検出する方法を示しています。
なぜCheck_xのテンプレート特化を作成しないのですか?
template<> bool Check_x(P1 p) { return true; }
template<> bool Check_x(P2 p) { return false; }
ヘック、考えてみると。タイプが2つしかない場合、これにテンプレートが必要なのはなぜですか?
抽象基底クラスの関数(x、X、y、Y)ですか、それともリファクタリングできますか?その場合は、Modern C ++ DesignのSUPERSUBCLASS()マクロと、この質問に対する回答のアイデアを使用できます。
コンパイル時に取得できます: 0-not_member、1-is_object、2-is_function
必要なクラスとメンバー-オブジェクトまたは関数: http://ideone.com/Fjm9u5
#include <iostream>
#include <type_traits>
#define IS_MEMBER(T1, M) \
struct { \
struct verystrangename1 { bool M; }; \
template<typename T> struct verystrangename2 : verystrangename1, public T { }; \
\
enum return_t { not_member, is_object, is_function }; \
template<typename T, typename = decltype(verystrangename2<T>::M)> constexpr return_t what_member() { return not_member; } \
template<typename T> typename std::enable_if<std::is_member_object_pointer<decltype(&T::M)>::value, return_t>::type constexpr what_member() { return is_object; } \
template<typename T> typename std::enable_if<std::is_member_function_pointer<decltype(&T::M)>::value, return_t>::type constexpr what_member() { return is_function; } \
constexpr operator return_t() { return what_member<T1>(); } \
}
struct t {
int aaa;
float bbb;
void func() {}
};
// Can't be in function
IS_MEMBER(t, aaa) is_aaa_member_of_t;
IS_MEMBER(t, ccc) is_ccc_member_of_t;
IS_MEMBER(t, func) is_func_member_of_t;
// known at compile time
enum { const_is_aaa_member_of_t = (int)is_aaa_member_of_t };
static constexpr int const_is_func_member_of_t = is_func_member_of_t;
int main() {
std::cout << std::boolalpha << "0 - not_member, 1 - is_object, 2 - is_function \n\n" <<
"is aaa member of t = " << is_aaa_member_of_t << std::endl <<
"is ccc member of t = " << is_ccc_member_of_t << std::endl <<
"is func member of t = " << is_func_member_of_t << std::endl <<
std::endl;
return 0;
}
結果:
0 - not_member, 1 - is_object, 2 - is_function
is aaa member of t = 1
is ccc member of t = 0
is func member of t = 2
クラス/構造体の場合:
struct t {
int aaa;
float bbb;
void func() {}
};