클래스에 주어진 서명의 멤버 함수가 있는지 확인
클래스에 주어진 서명의 특정 멤버 함수가 있는지 감지하기 위해 템플릿 트릭을 요청하고 있습니다.
문제는 http://www.gotw.ca/gotw/071.htm에 인용 된 것과 유사 하지만 동일하게 색칠합니다. Sutter의 책에서 클래스 C가 멤버 함수를 제공해야한다는 질문에 대답했습니다. 특별한 서명이없는 프로그램이 내 문제에서 클래스에 해당 기능이 있으면 좋을 것 "을 반드시해야합니다.
boost :: serialization이 직면 한 한 문제에 직면하고 채택한 솔루션이 마음에 들지 않습니다. 특정 멤버 함수를 정의하지 않는 한 기본적으로 특정 서명으로 자유 함수 (정의해야 함)를 호출하는 템플릿 함수화 특정 시그니처와 특정 유형의 2 개 많은 변수를 사용하는 "직렬. 즉 , 침입 및 비 침입 화를 모두 구현해야합니다 .
나는 두 가지 가지 그 솔루션을 좋아하지 않습니다.
- 방해하지 않을 것입니다. :: serialization 네임 스페이스에있는 전역 "serialize"함수를 재정의해야합니다. 따라서 클라이언트 코드에 스페이스 부스트 및 네임 스페이스 화를 열 수 있습니다!
- 그 혼란을 해결하기위한 스택은 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 기능을 사용하는 가능한 구현입니다. Mike 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
대답 타임 멤버 함수 내성에 대한이 질문에 대한 대답은 인기가 있습니다 다음 프로그램에서 볼 수있는 걸림돌이 있습니다.
#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;
}
GCC 4.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*()
. 그 이유 는 자체적으로는없고 템플릿 인스턴스화는 "상속을 수행하지"않기 때문입니다.
이 걸림은 "크기 () 트릭"을 사용하여 잘 작동합니다. SFINAE 접근 방식에 영향을 미치고 있습니다. 일부 T
멤버 함수가 있는지 여부 만-sense 합니다 mf
(예 : 이 답변 및 설명 참조). 그러나 그 T::mf
존재를 충분 하지 않습니다. 원하는 서명을 가지고 있는지 확인해야합니다. 여기에 그림 기술이 점수가 매겨는. 원하는 서명의 포인터 변형은 &T::mf
SFINAE 선호가 성공하기 위해 변수에 새겨 선호하는 유형의 변수에 있습니다 . 그러나이 템플릿 인스턴스화 기술 T::mf
은 상속 될 때 무효를 제공합니다 .
타임 내부 검사를위한 안전한 SFINAE 기술은 SFINAE 함수 템플릿 해상도가 의존하는 유형을 인스턴스화하기 위해 인수 내 T::mf
에서 사용하지 않습니다 &T::mf
. 대신 SFINAE 템플릿 함수는 오버로드 된 SFINAE 함수의 인수 유형으로 사용되는 정확한 유형 선언에만 의존 할 수 있습니다.
이 제약 조건을 따르는 질문에 대한 답변을 통해 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 :) */
합니다. 두 번째 주장은 다른 의미가 없습니다.
이 호출 test(0,0)
에 의지 해결 /* SFINAE operator-exists :) */
단지의 경우에 첫 번째 인수 0 satifies 과부하의 첫입니다 번째 변수 형식 매개 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*
원하는 서명이 있으면 여기에 예라고 말하고 다시 지원의 리턴 유형을 평가해야합니다. 더 이상 "재귀"가 없습니다 :입니다 std::true_type
.
컴파일러는 선택하지 않는 경우 /* SFINAE operator-exists :) */
호출에 test(0,0)
또는 선택하지 않는 /* SFINAE operator-has-correct-sig :) */
호출을 위해 test(&A::operator*)
, 다음 두 경우에 함께가는 /* SFINAE game over :( */
최종 반환 형식이다 std::false_type
.
다음은 다양한 사례 샘플에서 예상되는 답변을 생성하는 템플릿을 그래픽 테스트 프로그램입니다 (GCC 4.6.3 다시).
// 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, 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.
*/
//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)
한 멤버 함수의 이름이 있으면 충분합니다. (이 경우 멤버 함수가 함수 bla를 인스턴스화하지 못합니다 (함수 부분 전문화가 없기 때문에 작동하는 함수를 작성하는 것이 어렵습니다. 클래스를 사용할 수도 있습니다.). 또한 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);
}
Mike 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, "");
실행 가능한 예
같은 종류의 문제가 발생하여 여기에 제안 된 솔루션이 매우 흥미 롭다는 것을 알았습니다 ... 그러나 다음과 같은 솔루션에 대한 요구 사항이 있습니다.
- 상속 된 기능도 감지합니다.
- 비 C ++ 11 레디 컴파일러와 호환 가능합니다.
BOOST을 토론 -based으로 이와 같은 제안 제안하는 다른 프로그램을 찾았 습니다 . 다음은 boost :: 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
이 솔루션에는 두 가지 단점이 있습니다.
- 함수 쌍의 메소드 별 선언이 필요합니다.
- 특히 네임 스페이스 오염을 만듭니다. 예를 들어,
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
std :: is_member_function_pointer를 사용할 수 있습니다.
class A {
public:
void foo() {};
}
bool test = std::is_member_function_pointer<decltype(&A::foo)>::value;
방해 serialize
받지 않기 위해 코닉 조회 덕분에 직렬화되는 클래스 또는 아카이브 클래스의 네임 스페이스를 넣을 수도 있습니다 . 자세한 내용 은 자유 함수 재정의에 대한 네임 스페이스를 참조 하십시오. :-)
무료 기능을 구현하기 지정된 네임 스페이스를 여는 간단합니다. (예를 들어, 고유 한 유형 std
을 구현 swap
하기 위해 네임 스페이스를 열지하는데 마스킹 대신 Koenig.)
괜찮아. 두 번째 시도입니다. 이 중 하나가 마음에 들지보다 많은 아이디어를 찾고 있습니다.
Herb Sutter의 기사는 특성에 대해 이야기합니다. 따라서 기본 인스턴스화에 폴백 동작이있는 특성 클래스를 호출 할 수 있고 멤버 특성 함수가있는 각 클래스에 대해 클래스는 멤버를 호출하도록 특화되어 있습니다. 나는 허브의 기사가 많은 복사 및 추가 기술을 포함하지 않을 것이라고 생각합니다.
내가 말했듯이, 아마도 그 멤버를 구현하는 "태깅"클래스와 관련된 추가 작업을 않을 것입니다. 어떤 경우에 세 번째 해결을 찾고 있습니다 ....
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
, 문제의 clases는 Aa
상속이 멤버 우리가 찾고있는 그 특별한 한 것입니다.
에서 과 일치 여부 여부 한 C ++ (11 개) 클래스의 대체입니다. 또한 템플릿 메타 프로그래밍에 대한 이해를 위해 SFINAE-sizeof-trick의 기초를 밝게합니다.FooFinder
true_type
false_type
는 TypeSink
의 적분 결과 싱크 나중에 사용되는 템플릿 인 sizeof
형태를 형성하기 위해 주형에 실체화 연산자입니다.
이 match
기능은 일반 대응 물없이 다리 진 SFINAE 종류의 템플릿입니다. 따라서 인수 유형이 특수 유형과 일치하는 경우에만 인스턴스화 할 수 있습니다.
test
열거 형 선언과 함께 두 함수는 결국 중앙 SFINAE 패턴을 형성합니다. 줄임표를 사용하여 일반 false_type
인수와 더 구체적인 인수가있는 상대 항목을 우선 순위로 반환합니다 .
이 인스턴스화 할 수 있으려면 필요한 test
템플릿 인수 기능 T
은 match
인스턴스화하는 데 필요한 기능은 인스턴스화해야 TypeSink
합니다. 주의 할 점 &U::foo
은 함수로 래핑 되는 템플릿 인수 전문화 내에서 참조 되지 않은 멤버 조회가 여전히 발생하는 것입니다.
페이스 북 어리 석 감정을 사용하는 경우 다음과 같은 기능을 제공합니다.
#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;
}
구현 세부 사항은 이전 답변과 동일하지만 라이브러리를 사용하는 것이 더 간단합니다.
'IT' 카테고리의 다른 글
어디에서나 정수를 연결 (0) | 2020.07.10 |
---|---|
Docker Alpine 컨테이너에서 쉘 시작 (0) | 2020.07.10 |
CSS- 선택한 라디오 버튼 레이블의 스타일을 지정하는 방법 (0) | 2020.07.10 |
물음표 란 무엇입니까? (0) | 2020.07.10 |
카메라 롤에 이미지를 저장해야합니까? (0) | 2020.07.10 |