클래스에 특정 멤버 변수가 있는지 여부를 감지하는 방법은 무엇입니까?
알고리즘 템플릿 함수를 만들려면 템플릿 인수 인 클래스에서 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는 예일뿐입니다. 멤버를 알 수없는 클래스가 있습니다.
추신 : 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
}
유즈넷의 누군가에 대한 훌륭한 아이디어를 기반으로합니다.
참고 : HasX는 임의의 유형으로 x라는 데이터 또는 함수 멤버를 확인합니다. 멤버 이름을 소개하는 유일한 목적은 멤버 이름 조회에 대한 모호성을위한 것입니다. 멤버 유형은 중요하지 않습니다.
여기에보다 솔루션 간단 요하네스 SCHAUB-litb 의 하나 . 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<A>::value == true
및 HasX<B>::value == false
. 이유를 봅시다.
우선 거기 std::false_type
하고 std::true_type
있다 static constexpr bool
라는 부재 value
로 설정 false
하고 true
, 각각을,. 따라서 HasX
위 의 두 템플릿 은이 멤버를 상속합니다. (의 첫 번째 템플릿 std::false_type
과의 두 번째 템플릿 std::true_type
.)
간단하게 다음 위의 코드에 도달 할 때까지 단계별로 진행하겠습니다.
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)
유형을 생성합니다 . 예를 들어 유형은 은를 의미 합니다. 유사하게, 입력 보유하고 , 수단 및 수단 .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)
은 두 개의 하위 하위 사용을 순서대로 (즉, 먼저 1.2
와 다음 0
) 평가하는 (내장) 쉼표 연산자를 사용 하고 첫 번째 값을 버리고 두 번째 값을 생성합니다. 그 후,
int x = (1.2, 0);
다음과 같다
int x = 0;
이것과 함께 퍼팅 decltype
그 제공 decltype(1.2, 0)
수단 int
. 정말 특별한 것은 없습니다 1.2
또는 double
여기가. 예를 들어, true
유형 bool
과 decltype(true, 0)
수단 int
도 있습니다.
클래스 유형은 어떻습니까? instace의 경우 decltype(f, 0)
무엇 을 의미합니까? 그것이 여전히 의미가 예상되는 것은 당연 할 수 int
있습니다. 실제로, 와 을 취하고를 반환하는 위 의 함수와 유사한 쉼표 연산자에, 대한 over-로드가있을 수 있습니다 . 이 경우 입니다 .func
foo
int
char
decltype(foo, 0)
char
쉼표 연산자에 대한 향상된 사용을 어떻게 피할 수 있습니까? 음, void
피연산자에 대해 쉼표 연산자를 오버로드 할 수있는 방법이 무엇 이든 void
. 따라서 decltype((void) f, 0)
의미 int
합니다. 실제로 (void) f
캐스트 f
에서 foo
에 void
이는 기본적으로 아무것도하지 않습니다하지만 표현이 유형을 가진 것으로 간주해야 우리한다는 말 void
. 그런 다음 내장 연산자 쉼표가 일반적 ((void) f, 0)
결과 0
유형은 int
. 따라서 decltype((void) f, 0)
수단 int
.
이 캐스트가 정말 필요한가요? 이 쉼표 연산자는 더 많이 사용하지 않는 경우에는 음, foo
그리고 int
다음이 필요하지 않습니다. 유효한 연산자가 있는지 여부를 확인하기 위해 항상 소스 코드를 검사 할 수 있습니다. 그러나 이것이 바로 템플릿에 나타나고 템플릿 변수 인 f
유형 V
이있는 경우, 쉼표 연산자에 대한 오버로드가 존재하는지 여부가 더 이상 명확하지 않습니다. 일반적으로 우리는 캐스팅합니다.
결론 : decltype((void) f, 0)
은 멋진 표현 int
입니다.
5) SFINAE :
이것은 전체 과학입니다 ;-) 좋아 나는 과장하고 그것도 매우 간단하지 않습니다. 최소한 설명은 최소한 유지하겠습니다.
SFINAE는 대체 실패는 오류가 아닙니다. 즉, 템플릿 매개 변수가 유형으로 대체되면 잘못된 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 { };
다시 말하지만 SFINAE의 이점 decltype((void) T::x, 0)
이있는 멋진 int
말입니다.
T
가 유형으로 대체 잘못된 구성이 나타날 수 있습니다. 예를 들어 bool::x
는 유효한 C ++가 아니므 T
로 bool
in으로 대체 T::x
하면 잘못된 구문이 생성됩니다. SFINAE 원칙에 따라 컴파일러는 코드를 거부하지 않고 무시합니다. 보다 정확하게는 우리가 본 것처럼 HasX<bool>
실제로 HasX<bool, int>
. 에 대한 전문화를 U = int
선택해야하지만 인스턴스화하는 동안 컴파일러는 bool::x
템플릿 전문화가 존재하지 않는 것처럼 모두 찾아서 무시합니다.
이 시점에서 코드는 기본 템플릿 만 존재하는 위의 경우 (2)와 동일합니다. 따라서 HasX<bool, int>::value == false
.
이후에 대한 bool
보류에 사용 된 동일한 인수 는 유효하지 않은 구성입니다 ( 멤버 없음 ). 그러나 괜찮은 컴파일러는에 대한 전문화를 인스턴스화하는 데 많은 문제가 없습니다 (또는 더 정확하게는 for ). 따라서 .B
B::x
B
x
A::x
U = int
U = decltype((void) A::x, 0)
HasX<A>::value == true
6) 이름 해제 U
:
음, (5)의 코드를 다시 보면 U
이 선언 ( typename U
) 이 아닌 곳에서 사용되는 것을 알 수 있습니다 . 그런 다음 두 번째 템플릿 인수의 이름을 해제하고 코드를 얻을 수 있습니다.
이 질문 의 연장으로 닫힌 질문 에서 여기로 리디렉션 됩니다. 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 에라는 멤버 유형이 있으면 참이됩니다. 그러나 이것은 참조 유형 멤버를 감지하지 않습니다.
당신의 경우 헤더 파일을 추가하는 것으로 충분할 것입니다
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; }
이에 대한 두 번째 답변 (litb)은 구성원을 감지하는 방법을 보여줍니다.
Check_x의 템플릿 전문화를 생성하지 않는 이유는 무엇입니까?
template<> bool Check_x(P1 p) { return true; }
template<> bool Check_x(P2 p) { return false; }
도대체 내가 생각하면. 두 가지 유형 만있는 경우이를 위해 템플릿이 필요한 이유는 무엇입니까?
추상 기본 클래스의 함수 (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() {}
};
'ProgramingTip' 카테고리의 다른 글
webstorm에 console.log 또는 console.info에 대한 바로 가기가 있습니까? (0) | 2020.12.06 |
---|---|
클래스의 유형이 C ++의 특정 하위 클래스인지 어떻게 확인합니까? (0) | 2020.12.06 |
메모장 ++를 사용하여 원격 컴퓨터에서 파일을 편집하고 수 없습니다. (0) | 2020.12.06 |
Windows 7의 PowerShell : 일반 사용자를위한 Set-ExecutionPolicy (0) | 2020.12.06 |
웹 사이트에 '이메일로 공유'링크 추가 (0) | 2020.12.06 |