如果检查一类有一个成员的函数的一个特定签字
题
我请求用一个模板招来检测,如果一类具有特定成员职能的给予的签名。
问题是相似的一个引这里 http://www.gotw.ca/gotw/071.htm 但不是相同的:在该项目的萨特的书,他回答了问题,一个C类必须提供一个成员能与某一特定签名,否则该程序不会编译。在我的问题我需要做一些事情,如果一类具有这一职能,还有什么"别的东西".
一个类似的问题,面临着通过提升::化,但我不喜欢的解决方案,他们通过:一个模板功能,调用默认情况下免费的功能(那你有定义),与一个特定的签名,除非你定义的特定部件的功能(在他们的情况"列化",需要2参数的定型)与一个特定的签字,还有一个编译错误会发生。这是实现这两个侵入性和非侵入性的序列化。
我不喜欢这种解决办法的原因有两个:
- 不能侵入你必须复盖全球的"化"的功能,在增强::化的名字空间,这样,你有你的客户代码打开名字空间的提升和名称空间序列化!
- 堆叠以解决, 混乱是10到12个功能调用。
我需要定义一个自定义的行为类别,还没有这件功能和我体内的不同的名字空间(并且我不想要复盖全球的功能定义在一个名字空间,而我在另一个之一)
你可以给我一个暗示的解决这个难题?
解决方案
我不知道,如果我理解正确,但你可以利用SFINAE检测功能存在在编译时间。例如从我的代码(如果测试类件的功能位置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;
}
建立与海湾合作委员会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的方法,使用"的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*
有所需的签名,
然后再来评价的回报类型的超负荷。没有更多的
"递归"现在:它是 std::true_type
.
如果编译器没有选择 /* SFINAE operator-exists :) */
的
呼叫 test(0,0)
或者不选择 /* SFINAE operator-has-correct-sig :) */
呼叫 test(&A::operator*)
, 然后无论在哪种情况下,它与去
/* SFINAE game over :( */
并且最终返回的类型 std::false_type
.
这是一个测试程序,显示了产生预期的模板 答案在不同的试样的情况下(海湾合作委员会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、阶级、联盟或枚举:
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恶魔!):
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
在空间的类序列化,或者档案流,谢谢 柯尼希查找.看看 名字空间的用于功能复盖 更多的细节。:-)
开放任何给定名称空间来实现一个免费的功能是完全错误的。(例如,你不应该打开namespace std
实施 swap
为自己的种类,但应该使用柯尼希查找,而不是。)
好的。第二次尝试。好吧如果你不喜欢这一个,我在寻找更多的想法。
药萨特的条约谈判的特征。所以你可以有一个特征类,其默认的实例有后备的行为,并对每类成员的功能而存在,那么特征类为专门援引成员的功能。我相信草药的文章中提到的技术要做到这一因此,它不涉及很多复印,并粘贴。
就像我说的,不过,也许你不想的额外工作涉及"标记"类做实施该成员。在这种情况下,我期待在第三个解决方案。...
没有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;
}
虽然执行情况的详细信息是相同的,与以前的答复,使用图书馆是更简单。