체크 클래스 경우에는 회원수의 서명
문제
나는 템플릿릭을 감지하는 경우에는 클래스가 특정 회원수의 서명이 있습니다.
문제와 유사한 여기에 인용 http://www.gotw.ca/gotw/071.htm 하지만 같은:항목에서의 서터의 책이라 그가 대답하는 질문 클래스 C 를 제공해야 합 회원 기능으로 특히 서명은,다른 프로그램지 않을 컴파일.에 내 문제가 뭔가를 할 필요가있는 경우 클래스에는 함수,다른 사람이"다른 것".
유사한 문제에 직면했으로 부스트::직렬화하지만 나도 좋아하지 않는 솔루션을 채택:템플릿 함수를 호출하는 기본적으로 무료로 기능을(당신이 정의)특정 서명을 정의하지 않는 경우 특히 멤버 함수(에서 그들의 경우에는"직렬화"는 2 개의 매개변수는 특정 유형의)특정 서명은,다른 컴파일한 오류가 발생합니다.는 것은 모두 구현하기 위해 간섭 및 비-관입 serialization.
내가 좋아하지 않는 솔루션을 위한 두 가지 이유:
- 비 침해를 재정의해야 합니다 세계적"직렬화"기능에 있는 부스트::serialization 네임스페이스,그래서 당신은 당신의 클라이언트을 열어 코드 네임스페이스의 향상 및 네임스페이스는 직렬화!
- 스택을 해결하는 엉망이 되었 10 12 함수 호출.
나를 정의할 필요가 맞춤 동작하는 클래스하지 않는 회원 기능 및 내 엔터티 내에 있는 다른 네임스페이스(그리고 내가 원하지 않을 재정의 글로벌 함수 정의에 하나의 네임스페이스를 사용하는 동안 또 다른 하나)
너는 나에게 힌트를 이 수수께끼를 해결하기 위해?
해결책
나는 확실하지 않다면 나는 당신을 올바르게,하지만 당신을 악용할 수 있습 SFINAE 을 감지 기능이 존재에서 컴파일한다.예에서 나의 코드(을 테스트하는 경우스 클래스는 회원 기능 size_t used_memory()const).
template<typename T>
struct HasUsedMemoryMethod
{
template<typename U, size_t (U::*)() const> struct SFINAE {};
template<typename U> static char Test(SFINAE<U, &U::used_memory>*);
template<typename U> static int Test(...);
static const bool Has = sizeof(Test<T>(0)) == sizeof(char);
};
template<typename TMap>
void ReportMemUsage(const TMap& m, std::true_type)
{
// We may call used_memory() on m here.
}
template<typename TMap>
void ReportMemUsage(const TMap&, std::false_type)
{
}
template<typename TMap>
void ReportMemUsage(const TMap& m)
{
ReportMemUsage(m,
std::integral_constant<bool, HasUsedMemoryMethod<TMap>::Has>());
}
다른 팁
여기에 가능한 구현에 의존하는 C++11 특징이다.그것을 정확하게 감지 기능는 경우에도 그의 상속(과는 달리 해결책에서 허용되는 대답으로,마이크 Kinghan 에서 관찰 그의 대답이).
기능 이 조각에 대한 테스트라 serialize
:
#include <type_traits>
// Primary template with a static assertion
// for a meaningful error message
// if it ever gets instantiated.
// We could leave it undefined if we didn't care.
template<typename, typename T>
struct has_serialize {
static_assert(
std::integral_constant<T, false>::value,
"Second template parameter needs to be of function type.");
};
// specialization that does the checking
template<typename C, typename Ret, typename... Args>
struct has_serialize<C, Ret(Args...)> {
private:
template<typename T>
static constexpr auto check(T*)
-> typename
std::is_same<
decltype( std::declval<T>().serialize( std::declval<Args>()... ) ),
Ret // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>::type; // attempt to call it and see if the return type is correct
template<typename>
static constexpr std::false_type check(...);
typedef decltype(check<C>(0)) type;
public:
static constexpr bool value = type::value;
};
사용법:
struct X {
int serialize(const std::string&) { return 42; }
};
struct Y : X {};
std::cout << has_serialize<Y, int(const std::string&)>::value; // will print 1
허용되는 이 질문에 응답의 compiletime 멤버능 내성이 있지만,그것은 바르게 인기가가 될 수 있는 관찰 에서 다음과 같은 프로그램:
#include <type_traits>
#include <iostream>
#include <memory>
/* Here we apply the accepted answer's technique to probe for the
the existence of `E T::operator*() const`
*/
template<typename T, typename E>
struct has_const_reference_op
{
template<typename U, E (U::*)() const> struct SFINAE {};
template<typename U> static char Test(SFINAE<U, &U::operator*>*);
template<typename U> static int Test(...);
static const bool value = sizeof(Test<T>(0)) == sizeof(char);
};
using namespace std;
/* Here we test the `std::` smart pointer templates, including the
deprecated `auto_ptr<T>`, to determine in each case whether
T = (the template instantiated for `int`) provides
`int & T::operator*() const` - which all of them in fact do.
*/
int main(void)
{
cout << has_const_reference_op<auto_ptr<int>,int &>::value;
cout << has_const_reference_op<unique_ptr<int>,int &>::value;
cout << has_const_reference_op<shared_ptr<int>,int &>::value << endl;
return 0;
}
내장 GCC4.6.3,프로그램의 출력 110
-을 알리는 우리는
T = std::shared_ptr<int>
가 지 을 제공 int & T::operator*() const
.
지 않는 경우 이미 현명한 이 잡았다,다음에서 볼의 정의
std::shared_ptr<T>
헤더에 <memory>
이 빛을 발산.그
구현 std::shared_ptr<T>
에서 파생된 기본 클래스
속하는 operator*() const
.그래서 템플릿을 인스턴스화
SFINAE<U, &U::operator*>
을 구성하는"찾기"연산자를 위한
U = std::shared_ptr<T>
일어나지 않을 것이기 때문에, std::shared_ptr<T>
가 없
operator*()
자신의 오른쪽에있는 템플릿 및 인스턴스화하지 않는
"do 상속".
이러다가 영향을 미치지 않는 잘 알려진 SFINAE 접근 방식을 사용하여,"The sizeof()트릭",
검출을 위한 단지 여부 T
일부 회원 기능 mf
(예보이 답변 과 코멘트).지
을 수립 T::mf
이 존재하는 것은 자주(보통?) 충분히 좋지 않다:할
또한다는 것을 설치할 필요가가 원하는 서명이 있습니다.가
도시 기술 점수가 있습니다.이 pointerized 변종의 서명
이에 새겨진 매개 변수 서식 파일의 형식을 만족해야 합하여
&T::mf
에 대한 SFINAE 프로브를 성공합니다.하지만 이 템플릿화
기술 잘못된 응답을 때 T::mf
은 상속됩니다.
안전 SFINAE 기술에 대한 compiletime 성찰의 T::mf
피해야
의 사용 &T::mf
내 템플릿을 인수를 인스턴스화하는 유형에 따라 SFINAE
기능 템플릿 해상도에 따라 다릅니다.대신,SFINAE 템플릿능
해상도에 따라 달라질 수 있습에 따라 정확히 관련된 유형을 사용되는 선언
수 유형의 과부 SFINAE 프로브는 기능입니다.
의 방법으로는 질문에 대한 답변을 준수하여 이러한 제약 조건 나
설명한 compiletime 탐지 E T::operator*() const
, 대
임의 T
고 E
.같은 패턴이 적용됩니다 필요한 변경을 가하여
에 대한 조사를 제외하고는 다른 방법을 서명이 있습니다.
#include <type_traits>
/*! The template `has_const_reference_op<T,E>` exports a
boolean constant `value that is true iff `T` provides
`E T::operator*() const`
*/
template< typename T, typename E>
struct has_const_reference_op
{
/* SFINAE operator-has-correct-sig :) */
template<typename A>
static std::true_type test(E (A::*)() const) {
return std::true_type();
}
/* SFINAE operator-exists :) */
template <typename A>
static decltype(test(&A::operator*))
test(decltype(&A::operator*),void *) {
/* Operator exists. What about sig? */
typedef decltype(test(&A::operator*)) return_type;
return return_type();
}
/* SFINAE game over :( */
template<typename A>
static std::false_type test(...) {
return std::false_type();
}
/* This will be either `std::true_type` or `std::false_type` */
typedef decltype(test<T>(0,0)) type;
static const bool value = type::value; /* Which is it? */
};
이 솔루션에서 과부 SFINAE 기능 프로브 test()
은"호출됩
재귀적으로".(물론 그것은 없습니다 실제로에서 호출된 모든;그것은 단지는
익 유형의 가 호출에 의해 해결합니다.)
우리는 필요한 조사를 위해 적어도 하나가 두 개의 포인트 정보:
- 가
T::operator*()
에 존재하니까?하지 않을 경우,우리 모두 끝났습니다. - 어
T::operator*()
가 존재하는지,그것의 서명E T::operator*() const
?
우리는 답을 얻을 평가하여 반환한 유형의 단일화
하기 test(0,0)
.는 의해 수행:
typedef decltype(test<T>(0,0)) type;
이 전화를 해결할 수 있을 /* SFINAE operator-exists :) */
과
의 test()
, 거나,그것은 확인될 수 있습니다 /* SFINAE game over :( */
과부하 있습니다.그것을 해결할 수 없을 /* SFINAE operator-has-correct-sig :) */
과부하,
기 때문에 하나 예상하는 단 하나의 인수하고 우리는 전달하는 두.
왜 우리가 지나가 두?단순히 힘 해결책을 제외
/* SFINAE operator-has-correct-sig :) */
.두 번째 인수가 없는 다른 signifance.
호 test(0,0)
를 해결하 /* SFINAE operator-exists :) */
그
는 경우에는 먼저 인수는 0satifies 첫 번째 매개 변수는 유형의 과부하,
는 decltype(&A::operator*)
, 으로, A = T
.0 충족하는 형식
그냥 경우 T::operator*
가 존재합니다.
다고 가정해 봅시 컴파일러는 말이 그렇습니다.그것은 것으로
/* SFINAE operator-exists :) */
고 그것을 요구하는 결정을 반환의 유형
함수 호출하는 경우에는 decltype(test(&A::operator*))
-
익 유형의 또 다른 통화 test()
.
이번에 우리가 전달하는 단 하나의 인수, &A::operator*
, 우리는 지금
알고 있나 우리는 여기에 있지 못했을 것이다.전화 test(&A::operator*)
도
를 해결하거나 /* SFINAE operator-has-correct-sig :) */
또 다시
를 해결할 수 있을 /* SFINAE game over :( */
.호출이 일치합니다
/* SFINAE operator-has-correct-sig :) */
그냥 경우 &A::operator*
만족
하나의 매개변수 유형의 과부하는 E (A::*)() const
,
가 A = T
.
컴파일러는 네 말이 여기는 경우 T::operator*
가는 원하는 서명,
그리고 다시는 평가를 반환한 유형의 과부하.더 이상
"recursions"지금:그 std::true_type
.
컴파일러하지 않 선택 /* SFINAE operator-exists :) */
대
전화 test(0,0)
나지 않는 선택 /* SFINAE operator-has-correct-sig :) */
전화 test(&A::operator*)
, 다음 두 경우에 그와 함께 간다
/* SFINAE game over :( */
최종익 유형 std::false_type
.
여기에는 테스트 프로그램을 보여줍니다 템플릿 생성 예상 응답에 다양한 샘플의 경우(GCC4.6.3again).
// To test
struct empty{};
// To test
struct int_ref
{
int & operator*() const {
return *_pint;
}
int & foo() const {
return *_pint;
}
int * _pint;
};
// To test
struct sub_int_ref : int_ref{};
// To test
template<typename E>
struct ee_ref
{
E & operator*() {
return *_pe;
}
E & foo() const {
return *_pe;
}
E * _pe;
};
// To test
struct sub_ee_ref : ee_ref<char>{};
using namespace std;
#include <iostream>
#include <memory>
#include <vector>
int main(void)
{
cout << "Expect Yes" << endl;
cout << has_const_reference_op<auto_ptr<int>,int &>::value;
cout << has_const_reference_op<unique_ptr<int>,int &>::value;
cout << has_const_reference_op<shared_ptr<int>,int &>::value;
cout << has_const_reference_op<std::vector<int>::iterator,int &>::value;
cout << has_const_reference_op<std::vector<int>::const_iterator,
int const &>::value;
cout << has_const_reference_op<int_ref,int &>::value;
cout << has_const_reference_op<sub_int_ref,int &>::value << endl;
cout << "Expect No" << endl;
cout << has_const_reference_op<int *,int &>::value;
cout << has_const_reference_op<unique_ptr<int>,char &>::value;
cout << has_const_reference_op<unique_ptr<int>,int const &>::value;
cout << has_const_reference_op<unique_ptr<int>,int>::value;
cout << has_const_reference_op<unique_ptr<long>,int &>::value;
cout << has_const_reference_op<int,int>::value;
cout << has_const_reference_op<std::vector<int>,int &>::value;
cout << has_const_reference_op<ee_ref<int>,int &>::value;
cout << has_const_reference_op<sub_ee_ref,int &>::value;
cout << has_const_reference_op<empty,int &>::value << endl;
return 0;
}
이 있는 새로운 결함이는 아이디어?할 수 있습 그것은이들을 일반적인 없이 다시 한번 떨어지는 파울의 하다가 그것을 피할 수?
여기에 몇 가지 사용량 조각:*용기를 위해 모든 이가 더 멀리
체크에 대한 구성원 x
에서 지정된 클래스입니다.수 var,func,클래스,조합,또는 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;
나
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.
*/
//Variadic to force ambiguity of class members. C++11 and up.
template <typename... Args> struct ambiguate : public Args... {};
//Non-variadic version of the line above.
//template <typename A, typename B> struct ambiguate : public A, public B {};
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)
이 충분해야 하는 경우에,당신의 이름을 알고 있는 멤버능합니다.(이 경우는,기능 즐 실패를 인스턴스화면 구성원이 없는 기능(쓰기를 작동 하는 것은 어쨌든 힘든 부족이 있기 때문이의 함수를 부분적인 전문화를 만들었습니다.를 사용해야 할 수 있습니다 템플릿 클래스)또한 활성화 구조체(과 유사 enable_if)또한 수 템플릿의 종류에 당신이 원하는 기능을 수 있습니다.
template <typename T, int (T::*) ()> struct enable { typedef T type; };
template <typename T> typename enable<T, &T::i>::type bla (T&);
struct A { void i(); };
struct B { int i(); };
int main()
{
A a;
B b;
bla(b);
bla(a);
}
여기에는 간단한 걸 마이크 Kinghan 의 대답이다.이것은 감지 상속된 방법입니다.그것은 또한 확인 정확한 서명(과는 달리 jrok 의 접근할 수 있는 인수에 변환).
template <class C>
class HasGreetMethod
{
template <class T>
static std::true_type testSignature(void (T::*)(const char*) const);
template <class T>
static decltype(testSignature(&T::greet)) test(std::nullptr_t);
template <class T>
static std::false_type test(...);
public:
using type = decltype(test<C>(nullptr));
static const bool value = type::value;
};
struct A { void greet(const char* name) const; };
struct Derived : A { };
static_assert(HasGreetMethod<Derived>::value, "");
실행 가능한 예
당신이 사용할 수 있는 std::is_member_function_pointer
class A {
public:
void foo() {};
}
bool test = std::is_member_function_pointer<decltype(&A::foo)>::value;
온와 같은 종류의 문제를 신고 제안된 솔루션에 여기에 매우 흥미로운...그러나 요구하는 솔루션:
- 을 감지 상속된 기능뿐만 아니라;
- 과 호환되는 비 C++11 준비를 컴파일러(아 decltype)
을 발견 또 다른 스레드 제안이 뭔가를 기반으로, 부 토론.여기에 일반화의 제안 솔루션으로 두 매크로를 선언한 특성을 클래스에는 다음의 모델 부스트::has_* 클래스입니다.
#include <boost/type_traits/is_class.hpp>
#include <boost/mpl/vector.hpp>
/// Has constant function
/** \param func_ret_type Function return type
\param func_name Function name
\param ... Variadic arguments are for the function parameters
*/
#define DECLARE_TRAITS_HAS_FUNC_C(func_ret_type, func_name, ...) \
__DECLARE_TRAITS_HAS_FUNC(1, func_ret_type, func_name, ##__VA_ARGS__)
/// Has non-const function
/** \param func_ret_type Function return type
\param func_name Function name
\param ... Variadic arguments are for the function parameters
*/
#define DECLARE_TRAITS_HAS_FUNC(func_ret_type, func_name, ...) \
__DECLARE_TRAITS_HAS_FUNC(0, func_ret_type, func_name, ##__VA_ARGS__)
// Traits content
#define __DECLARE_TRAITS_HAS_FUNC(func_const, func_ret_type, func_name, ...) \
template \
< typename Type, \
bool is_class = boost::is_class<Type>::value \
> \
class has_func_ ## func_name; \
template<typename Type> \
class has_func_ ## func_name<Type,false> \
{public: \
BOOST_STATIC_CONSTANT( bool, value = false ); \
typedef boost::false_type type; \
}; \
template<typename Type> \
class has_func_ ## func_name<Type,true> \
{ struct yes { char _foo; }; \
struct no { yes _foo[2]; }; \
struct Fallback \
{ func_ret_type func_name( __VA_ARGS__ ) \
UTILITY_OPTIONAL(func_const,const) {} \
}; \
struct Derived : public Type, public Fallback {}; \
template <typename T, T t> class Helper{}; \
template <typename U> \
static no deduce(U*, Helper \
< func_ret_type (Fallback::*)( __VA_ARGS__ ) \
UTILITY_OPTIONAL(func_const,const), \
&U::func_name \
>* = 0 \
); \
static yes deduce(...); \
public: \
BOOST_STATIC_CONSTANT( \
bool, \
value = sizeof(yes) \
== sizeof( deduce( static_cast<Derived*>(0) ) ) \
); \
typedef ::boost::integral_constant<bool,value> type; \
BOOST_STATIC_CONSTANT(bool, is_const = func_const); \
typedef func_ret_type return_type; \
typedef ::boost::mpl::vector< __VA_ARGS__ > args_type; \
}
// Utility functions
#define UTILITY_OPTIONAL(condition, ...) UTILITY_INDIRECT_CALL( __UTILITY_OPTIONAL_ ## condition , ##__VA_ARGS__ )
#define UTILITY_INDIRECT_CALL(macro, ...) macro ( __VA_ARGS__ )
#define __UTILITY_OPTIONAL_0(...)
#define __UTILITY_OPTIONAL_1(...) __VA_ARGS__
이 매크로를 확장 특성 등 다음과 같은 시제품:
template<class T>
class has_func_[func_name]
{
public:
/// Function definition result value
/** Tells if the tested function is defined for type T or not.
*/
static const bool value = true | false;
/// Function definition result type
/** Type representing the value attribute usable in
http://www.boost.org/doc/libs/1_53_0/libs/utility/enable_if.html
*/
typedef boost::integral_constant<bool,value> type;
/// Tested function constness indicator
/** Indicates if the tested function is const or not.
This value is not deduced, it is forced depending
on the user call to one of the traits generators.
*/
static const bool is_const = true | false;
/// Tested function return type
/** Indicates the return type of the tested function.
This value is not deduced, it is forced depending
on the user's arguments to the traits generators.
*/
typedef func_ret_type return_type;
/// Tested function arguments types
/** Indicates the arguments types of the tested function.
This value is not deduced, it is forced depending
on the user's arguments to the traits generators.
*/
typedef ::boost::mpl::vector< __VA_ARGS__ > args_type;
};
그래서 어떤 것은 일반적인 사용 할 수 있습니까?
// We enclose the traits class into
// a namespace to avoid collisions
namespace ns_0 {
// Next line will declare the traits class
// to detect the member function void foo(int,int) const
DECLARE_TRAITS_HAS_FUNC_C(void, foo, int, int);
}
// we can use BOOST to help in using the traits
#include <boost/utility/enable_if.hpp>
// Here is a function that is active for types
// declaring the good member function
template<typename T> inline
typename boost::enable_if< ns_0::has_func_foo<T> >::type
foo_bar(const T &_this_, int a=0, int b=1)
{ _this_.foo(a,b);
}
// Here is a function that is active for types
// NOT declaring the good member function
template<typename T> inline
typename boost::disable_if< ns_0::has_func_foo<T> >::type
foo_bar(const T &_this_, int a=0, int b=1)
{ default_foo(_this_,a,b);
}
// Let us declare test types
struct empty
{
};
struct direct_foo
{
void foo(int,int);
};
struct direct_const_foo
{
void foo(int,int) const;
};
struct inherited_const_foo :
public direct_const_foo
{
};
// Now anywhere in your code you can seamlessly use
// the foo_bar function on any object:
void test()
{
int a;
foo_bar(a); // calls default_foo
empty b;
foo_bar(b); // calls default_foo
direct_foo c;
foo_bar(c); // calls default_foo (member function is not const)
direct_const_foo d;
foo_bar(d); // calls d.foo (member function is const)
inherited_const_foo e;
foo_bar(e); // calls e.foo (inherited member function)
}
이를 위해 우리는 우리를 사용해야 합니다:
- 기능 템플릿의 과대 적재 서로 다른 유형을 반환 여부에 따라 방법을 사용하실 수 있습니다
- 을 유지하는 메타-조건문에서
type_traits
헤더,우리가 원할 것을 반환true_type
나false_type
에서 우리의 오버로드 - 선언
true_type
과부하 기대int
고false_type
과부하 기대됩 매개 변수를 악용: "의 가장 낮은 우선순위 생략 변환 과부하는 해결책" - 정의에서는 템플 사양
true_type
기능을 사용할 것입declval
고decltype
할 수 있도록 검색 기능 독립적인 수익의 유형에 차이나 오버로드 방법을 사
당신이 볼 수 있는 라이브의 예이 기. 하지만 나 또한 그것을 설명 아래:
확인하고 싶어한 존재라는 기능 test
는 입력 컨버터블서 int
,그리고 선언할 필요가 이러한 두 가지 기능:
template <typename T, typename S = decltype(declval<T>().test(declval<int>))> static true_type hasTest(int);
template <typename T> static false_type hasTest(...);
decltype(hasTest<a>(0))::value
가true
(참고가 없을 만들 필요한 특별한 기능을 가진 거래void a::test()
과부하,void a::test(int)
가능)decltype(hasTest<b>(0))::value
가true
(기int
은 가변기double
int b::test(double)
은 허용,독립적의 반환형)decltype(hasTest<c>(0))::value
가false
(c
하지 않는 방법이라는 이름test
는 것을 허용 입력 컨버터블서int
그 때문에 이것은 허용되지 않습니다)
이 솔루션은 2 단점이 있:
- 야 합당한 방법의 선언을 한 쌍의 기능
- 성 네임스페이스 오염을 경우에 특히 우리가 원하는 테스트를 위한 비슷한 이름은,예를 들어 우리가 무엇을 할 것 이라고 이름는 기능을 테스트하고 싶어한
test()
방법?
그래서 그것의 중요한 이러한 기능이 선언할에 상세정보 네임스페이스,또는 이상적인 경우에만 사용하는 등,그들은 선언해야 합로 개인적으로는 클래스입니다.저쪽 끝으로 나는 기록 매크로 당신을 도와 추상적인 이 정보:
#define FOO(FUNCTION, DEFINE) template <typename T, typename S = decltype(declval<T>().FUNCTION)> static true_type __ ## DEFINE(int); \
template <typename T> static false_type __ ## DEFINE(...); \
template <typename T> using DEFINE = decltype(__ ## DEFINE<T>(0));
를 사용할 수 있습니다 다음과 같:
namespace details {
FOO(test(declval<int>()), test_int)
FOO(test(), test_void)
}
이후 호출하는 details::test_int<a>::value
나 details::test_void<a>::value
수 true
나 false
의 목적을 위해 인라인 코드 또는 메타-프로그래밍입니다.
비-관입할 수 있습니다 serialize
에서의 네임스페이스 클래스는 연재된,또는 보관의 클래스,감사 Koenig 조회.보 네임스페이스에 대한 자유로운 기능 재정의 자세한 내용은.:-)
열어 주어진 네임스페이스를 구현하는 자유로운 기능은 완전히 잘못된 것입니다.(예를 들어,당신이하지 않을 열 네임스페이스 std
를 구현하는 swap
에 대한 당신의 자신의 형식만을 사용해야 Koenig 조회 대신 합니다.)
괜찮습니다.두 번째 시도합니다.그것은 그렇지 않으면 다음과 같이 하나,내가 찾는 것에 대한 더 많은 정보를 얻으시기 바랍니다.
나물 서터의 문서에 대해 이야기 특성.는지 이해할 수 있도록 특성을 클래스의 인스턴스화하는 기본은 대체 행동을,그리고 각 클래스에 대한 당신의 멤버 함수 존재,그 특성을 클래스 전문를 호출하는 멤버 함수입니다.내가 믿고 나물의 문서에서 언급하는 기술이 이를 수행되지 않도록의 많은 참여 복사 및 붙여넣기.
내가 말했듯이,하지만,아마도 당신을 원하지 않은 추가 작업과 관련된"태그"는 클래스를 구현하는 회원입니다.어떤 경우에,내가 찾는 것에서 세 번째는 솔루션입니다....
없는 C++11(지원decltype
다)이 작동 수 있습니다:
SSCCE
#include <iostream>
using namespace std;
struct A { void foo(void); };
struct Aa: public A { };
struct B { };
struct retA { int foo(void); };
struct argA { void foo(double); };
struct constA { void foo(void) const; };
struct varA { int foo; };
template<typename T>
struct FooFinder {
typedef char true_type[1];
typedef char false_type[2];
template<int>
struct TypeSink;
template<class U>
static true_type &match(U);
template<class U>
static true_type &test(TypeSink<sizeof( matchType<void (U::*)(void)>( &U::foo ) )> *);
template<class U>
static false_type &test(...);
enum { value = (sizeof(test<T>(0, 0)) == sizeof(true_type)) };
};
int main() {
cout << FooFinder<A>::value << endl;
cout << FooFinder<Aa>::value << endl;
cout << FooFinder<B>::value << endl;
cout << FooFinder<retA>::value << endl;
cout << FooFinder<argA>::value << endl;
cout << FooFinder<constA>::value << endl;
cout << FooFinder<varA>::value << endl;
}
어떻게 그것이 바라는 작품
A
, Aa
고 B
은 다시금에서 질문 Aa
특별한 하나되는 상속되는 회원은 우리가 찾고 있습니다.
에 FooFinder
이 true_type
고 false_type
는 대체 기자는 C++11 클래스입니다.또한 이해를 위한 템플릿의 메타 프로그래밍,들을 매우 기초의 SFINAE-sizeof-다.
이 TypeSink
는 템플릿 구조체는 나중에 사용하는 싱크대의 완전한 결과의 sizeof
운전자 템플릿으로 인스턴스화하는 형태로는 유형입니다.
이 match
기능이 다른 SFINAE 의 종류는 템플릿에 남지 않고 제네릭합니다.할 수 있습 따라서만 인스턴스화하는 경우 형식의 인수와 일치합형 그것을 전문화되었습니다.
모두 test
과 함께 열거 선언을 마지막으로 양식을 중앙 SFINAE 패턴이다.가 일반 중 하나를 사용하여 선호를 반환하는 false_type
및 대응 구체적인 인수를 우선적으로 적용됩니다.
할 수 있 인스턴스화 test
함수와 템플릿을 인수 T
, 이, match
능 인스턴스화되어야 합니다,반환 형식으로 필요한 인스턴스화 TypeSink
다.경고는 &U::foo
, 되고,포장에 함수의 인자이 지 라고서 템플릿을 인수 전문성,그래서 상속된 구성원회는 여전히 발생한다.
나는 당신이 찾고있는 대답이 여기에있다.
http://www.martinecker.com/wiki/index.php?title=Detecting_the_Existence_of_Operators_at_Compile-Time
그리고 약간 더 많은으로 가득 예기
내가 사용하는 기술을의 존재를 검출하는 지원 ostream 연산자 << 클래스에서 질문과 다음 생성하는 다른 비트의 코드를 따라.
나는 그것을 믿지 않았다이 가능했을 찾기 전에 연결된 솔루션을 하지만 그것은 매우 깔끔한다.시간을 보내고 이해 코드를 매우 가치가있는 동안.
브
를 사용하는 경우 facebook 어리석은 자는 상자의 매크로 당신을 도울:
#include <folly/Traits.h>
namespace {
FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(has_test_traits, test);
} // unnamed-namespace
void some_func() {
cout << "Does class Foo have a member int test() const? "
<< boolalpha << has_test_traits<Foo, int() const>::value;
}
하지만 구현 정보의 이전과 동일한 답변,라이브러리를 사용하는 것은 간단합니다.