アルゴリズムテンプレート関数を作成するには、テンプレート引数であるクラスの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ではコンパイルしません。VisualStudioでは、次のテンプレートを使用できます。
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は例にすぎません。メンバーが不明なクラスが存在する可能性があります。
追伸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 { };
_
Update:簡単な例と、この仕組みの説明。
これらのタイプの場合:
_struct A { int x; };
struct B { int y; };
_
_HasX<A>::value == true
_と_HasX<B>::value == false
_があります。理由を見てみましょう。
最初に、_std::false_type
_および_std::true_type
_には、それぞれvalue
およびfalse
に設定されるtrue
という名前の_static constexpr bool
_メンバーがあることを思い出してください。したがって、上記の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<bool, double>::value == false
_および_HasX<bool, int>::value == false
_から派生します。
2)U
のデフォルト設定:
_// Primary template
template <typename T, typename U = int>
struct HasX : std::false_type { };
_
U
のデフォルトがint
であるとすると、_Has<bool>
_は実際には_HasX<bool, int>
_を意味し、したがって_HasX<bool>::value == HasX<bool, int>::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<T, U>
_は_std::false_type
_から派生します。ただし、_U = int
_から派生した_std::true_type
_の特殊化が存在します。したがって、_HasX<bool, double>::value == false
_が_HasX<bool, int>::value == true
_です。
U
のデフォルトのおかげで、_HasX<bool>::value == HasX<bool, int>::value == true
_。
4)decltype
およびint
を言うための派手な方法:
ここで少し余談しますが、どうか、我慢してください。
基本的に(これは完全に正しいわけではありません)、decltype(expression)
はexpressionのタイプを生成します。たとえば、_0
_はint
型であるため、decltype(0)
はint
を意味します。同様に、_1.2
_の型はdouble
であるため、decltype(1.2)
はdouble
を意味します。
次の宣言を持つ関数を考えてみましょう。
_char func(foo, int);
_
ここで、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
です。
コンマ演算子のオーバーロードの使用をどのように回避できますか?まあ、void
オペランドのコンマ演算子をオーバーロードする方法はなく、何でもvoid
にキャストできます。したがって、decltype((void) f, 0)
はint
を意味します。実際、_(void) f
_はf
をfoo
からvoid
にキャストしますが、これは基本的に、式がvoid
型を持っていると見なさなければならないことを示すだけです。次に、組み込み演算子のコンマが使用され、_((void) f, 0)
_はint
型の_0
_になります。したがって、decltype((void) f, 0)
はint
を意味します。
このキャストは本当に必要ですか?まあ、foo
とint
をとるコンマ演算子のオーバーロードがなければ、これは必要ありません。ソースコードをいつでも調べて、そのような演算子があるかどうかを確認できます。ただし、これがテンプレートに表示され、f
の型パラメーターがV
である場合、そのようなコンマ演算子のオーバーロードが存在するかどうかは明確ではありません(または知ることさえできません)。とにかく一般的にするためにキャストします。
一番下の行:decltype((void) f, 0)
は、int
を言うのに凝った方法です。
5)SFINAE:
これは完全な科学です;-) OK私は大げさですが、それは非常に単純でもありません。そのため、説明は最小限に留めます。
SFINAEはSubstitution Failure is Not Errorの略です。テンプレートパラメーターが型で置換されると、不正なC++コードが表示される可能性がありますが、いくつかの状況では、コンパイルを中止する代わりに、コンパイラーは単に違反コードをあたかも無視しますありませんでした。それが私たちのケースにどのように適用されるか見てみましょう:
_// Primary template
template <typename T, typename U = int>
struct HasX : std::false_type { };
// Specialization for U = int
template <typename T>
struct HasX <T, decltype((void) T::x, 0)> : std::true_type { };
_
ここでも、decltype((void) T::x, 0)
はint
を言うのに派手な方法ですが、SFINAEの利点があります。
T
が型に置換されると、無効な構成体が表示される場合があります。たとえば、_bool::x
_は有効なC++ではないため、_T::x
_でT
をbool
に置き換えると、無効な構文が生成されます。 SFINAEの原則では、コンパイラはコードを拒否せず、単に(の一部を)無視します。より正確には、見たように_HasX<bool>
_は実際には_HasX<bool, int>
_を意味します。 _U = int
_の特殊化を選択する必要がありますが、インスタンス化中にコンパイラーは_bool::x
_を検出し、テンプレートの特殊化が存在しないかのように完全に無視します。
この時点で、コードは本質的に上記のケース(2)と同じであり、プライマリテンプレートのみが存在します。したがって、_HasX<bool, int>::value == false
_。
_B::x
_は無効な構造であるため、bool
に使用されるのと同じ引数がB
に適用されます(B
にはメンバーx
がありません)。ただし、_A::x
_はOKであり、_U = int
_(より正確には、U = decltype((void) A::x, 0)
)の特殊化をインスタンス化する際にコンパイラーは問題を認識しません。したがって、_HasX<A>::value == true
_。
6)U
の名前の変更:
さて、(5)のコードをもう一度見ると、名前U
は宣言(_typename U
_)以外では使用されていないことがわかります。次に、2番目のテンプレート引数の名前を解除し、この投稿の上部に示されているコードを取得します。
私はここからリダイレクトされました question これはこれの複製として閉じられました。古いスレッドであることは知っていますが、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 { };
それでおしまい。そして、これがどのように使用されるかです( live example ):
#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;
_
メンバーenum 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;
_
OR
_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;
};
_
マクロス(El Diablo!):(
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を組み合わせることで、ほとんどのコンセプトに対応できます。
これに対する2番目の回答(litb's)は、メンバーを検出する方法を示しています。
このような専門化を使用してみませんか:
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; }
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() {}
};