Is it possible to write a template that changes behavior depending on if a certain member function is defined on a class?
是否可以编写一个模板,根据类上是否定义了某个成员函数来更改行为?
Here's a simple example of what I would want to write:
这里有一个我想写的简单例子:
template<class T>
std::string optionalToString(T* obj)
{
if (FUNCTION_EXISTS(T->toString))
return obj->toString();
else
return "toString not defined";
}
So, if class T
has toString()
defined, then it uses it; otherwise, it doesn't. The magical part that I don't know how to do is the "FUNCTION_EXISTS" part.
如果类T定义了toString()那么它就会使用它;否则,它不喜欢。我不知道该怎么做的神奇部分是“FUNCTION_EXISTS”部分。
23 个解决方案
#1
276
Yes, with SFINAE you can check if a given class does provide a certain method. Here's the working code:
是的,使用SFINAE您可以检查给定的类是否提供了特定的方法。这里的工作代码:
#include <iostream>
struct Hello
{
int helloworld() { return 0; }
};
struct Generic {};
// SFINAE test
template <typename T>
class has_helloworld
{
typedef char one;
typedef long two;
template <typename C> static one test( typeof(&C::helloworld) ) ;
template <typename C> static two test(...);
public:
enum { value = sizeof(test<T>(0)) == sizeof(char) };
};
int main(int argc, char *argv[])
{
std::cout << has_helloworld<Hello>::value << std::endl;
std::cout << has_helloworld<Generic>::value << std::endl;
return 0;
}
I've just tested it with Linux and gcc 4.1/4.3. I don't know if it's portable to other platforms running different compilers.
我刚刚用Linux和gcc 4.1/4.3测试了它。我不知道它是否可以移植到运行不同编译器的其他平台。
#2
222
This question is old, but with C++11 we got a new way to check for a functions existence (or existence of any non-type member, really), relying on SFINAE again:
这个问题很老了,但是有了c++ 11,我们有了一种新的方法来检查函数的存在性(或者任何非类型成员的存在性,真的),再次依赖于SFINAE:
template<class T>
auto serialize_imp(std::ostream& os, T const& obj, int)
-> decltype(os << obj, void())
{
os << obj;
}
template<class T>
auto serialize_imp(std::ostream& os, T const& obj, long)
-> decltype(obj.stream(os), void())
{
obj.stream(os);
}
template<class T>
auto serialize(std::ostream& os, T const& obj)
-> decltype(serialize_imp(os, obj, 0), void())
{
serialize_imp(os, obj, 0);
}
生活的例子。
Now onto some explanations. First thing, I use expression SFINAE to exclude the serialize(_imp)
functions from overload resolution, if the first expression inside decltype
isn't valid (aka, the function doesn't exist).
现在在一些解释。首先,我使用表达式SFINAE从重载解析中排除序列化(_imp)函数,如果decltype里面的第一个表达式不是有效的(比如,这个函数不存在)。
The void()
is used to make the return type of all those functions void
.
void()用于使所有这些函数的返回类型无效。
The 0
argument is used to prefer the os << obj
overload if both are available (literal 0
is of type int
and as such the first overload is a better match).
如果两者都可用,则使用0参数来选择os << obj重载(文字0是int类型,因此第一个重载是更好的匹配)。
Now, you probably want a trait to check if a function exists. Luckily, it's easy to write that. Note, though, that you need to write a trait yourself for every different function name you might want.
现在,您可能想要一个特性来检查一个函数是否存在。幸运的是,很容易写出来。但是,请注意,您需要为您想要的每个不同的函数名编写一个特性。
#include <type_traits>
template<class>
struct sfinae_true : std::true_type{};
namespace detail{
template<class T, class A0>
static auto test_stream(int)
-> sfinae_true<decltype(std::declval<T>().stream(std::declval<A0>()))>;
template<class, class A0>
static auto test_stream(long) -> std::false_type;
} // detail::
template<class T, class Arg>
struct has_stream : decltype(detail::test_stream<T, Arg>(0)){};
生活的例子。
And on to explanations. First, sfinae_true
is a helper type, and it basically amounts to the same as writing decltype(void(std::declval<T>().stream(a0)), std::true_type{})
. The advantage is simply that it's shorter.
Next, the struct has_stream : decltype(...)
inherits from either std::true_type
or std::false_type
in the end, depending on whether the decltype
check in test_stream
fails or not.
Last, std::declval
gives you a "value" of whatever type you pass, without you needing to know how you can construct it. Note that this is only possible inside an unevaluated context, such as decltype
, sizeof
and others.
和解释。首先,sfinae_true是一个助手类型,它基本上等同于编写decltype(void(std::declval
Note that decltype
is not necessarily needed, as sizeof
(and all unevaluated contexts) got that enhancement. It's just that decltype
already delivers a type and as such is just cleaner. Here's a sizeof
version of one of the overloads:
请注意,并不一定需要decltype,因为sizeof(以及所有未被评估的上下文)得到了这种增强。仅仅是解密已经提供了一种类型,而且这是更清洁的。以下是其中一个重载的sizeof版本:
template<class T>
void serialize_imp(std::ostream& os, T const& obj, int,
int(*)[sizeof((os << obj),0)] = 0)
{
os << obj;
}
生活的例子。
The int
and long
parameters are still there for the same reason. The array pointer is used to provide a context where sizeof
can be used.
由于相同的原因,int和long参数仍然存在。数组指针用于提供可以使用sizeof的上下文。
#3
153
C++ allows SFINAE to be used for this (notice that with C++11 features this is simplier because it supports extended SFINAE on nearly arbitrary expressions - the below was crafted to work with common C++03 compilers):
c++允许为此使用SFINAE(注意,对于c++ 11的特性,这很简单,因为它支持几乎任意表达式上的扩展SFINAE——下面的代码是为与普通c++ 03编译器一起工作而编写的):
#define HAS_MEM_FUNC(func, name) \
template<typename T, typename Sign> \
struct name { \
typedef char yes[1]; \
typedef char no [2]; \
template <typename U, U> struct type_check; \
template <typename _1> static yes &chk(type_check<Sign, &_1::func > *); \
template <typename > static no &chk(...); \
static bool const value = sizeof(chk<T>(0)) == sizeof(yes); \
}
the above template and macro tries to instantiate a template, giving it a member function pointer type, and the actual member function pointer. If the types to not fit, SFINAE causes the template to be ignored. Usage like this:
上面的模板和宏尝试实例化一个模板,给它一个成员函数指针类型,以及实际的成员函数指针。如果类型不适合,SFINAE将导致模板被忽略。用法如下:
HAS_MEM_FUNC(toString, has_to_string);
template<typename T> void
doSomething() {
if(has_to_string<T, std::string(T::*)()>::value) {
...
} else {
...
}
}
But note that you cannot just call that toString
function in that if branch. since the compiler will check for validity in both branches, that would fail for cases the function doesnt exist. One way is to use SFINAE once again (enable_if can be gotten from boost too):
但是注意,不能只在if分支中调用toString函数。由于编译器将检查两个分支中的有效性,因此在函数不存在的情况下会失败。一种方法是再次使用SFINAE (enable_if也可以从boost获得):
template<bool C, typename T = void>
struct enable_if {
typedef T type;
};
template<typename T>
struct enable_if<false, T> { };
HAS_MEM_FUNC(toString, has_to_string);
template<typename T>
typename enable_if<has_to_string<T,
std::string(T::*)()>::value, std::string>::type
doSomething(T * t) {
/* something when T has toString ... */
return t->toString();
}
template<typename T>
typename enable_if<!has_to_string<T,
std::string(T::*)()>::value, std::string>::type
doSomething(T * t) {
/* something when T doesnt have toString ... */
return "T::toString() does not exist.";
}
Have fun using it. The advantage of it is that it also works for overloaded member functions, and also for const member functions (remember using std::string(T::*)() const
as the member function pointer type then!).
玩得开心使用它。它的优点是它也适用于重载的成员函数,也适用于const成员函数(请记住使用std::string(T::*) const作为成员函数指针类型!)
#4
52
Though this question is two years old, I'll dare to add my answer. Hopefully it will clarify the previous, indisputably excellent, solution. I took the very helpful answers of Nicola Bonelli and Johannes Schaub and merged them into a solution that is, IMHO, more readable, clear and does not require the typeof
extension:
虽然这个问题已经有两年了,但我敢补充我的答案。希望它能澄清之前无可争议的优秀解决方案。我把Nicola Bonelli和Johannes Schaub的非常有帮助的答案合并到一个解决方案中,IMHO,更加可读,更清晰,不需要扩展类型:
template <class Type>
class TypeHasToString
{
// This type won't compile if the second template parameter isn't of type T,
// so I can put a function pointer type in the first parameter and the function
// itself in the second thus checking that the function has a specific signature.
template <typename T, T> struct TypeCheck;
typedef char Yes;
typedef long No;
// A helper struct to hold the declaration of the function pointer.
// Change it if the function signature changes.
template <typename T> struct ToString
{
typedef void (T::*fptr)();
};
template <typename T> static Yes HasToString(TypeCheck< typename ToString<T>::fptr, &T::toString >*);
template <typename T> static No HasToString(...);
public:
static bool const value = (sizeof(HasToString<Type>(0)) == sizeof(Yes));
};
I checked it with gcc 4.1.2. The credit goes mainly to Nicola Bonelli and Johannes Schaub, so give them a vote up if my answer helps you :)
我用gcc 4.1.2检查了一下。这主要归功于尼古拉·波内利(Nicola Bonelli)和约翰内斯·绍布(Johannes Schaub),所以如果我的回答对你有帮助的话,请给他们投票。
#5
35
Detection toolkit
N4502 proposes a detection tookit for inclusion into the C++17 standard library which can solve the problem in a somewhat elegant manner. Moreover, it just got accepted into the library fundamentals TS v2. It introduces some metafunctions, including std::is_detected
which can be used to easily write type or function detection metafunctions on the top of it. Here is how you could use it:
N4502提出了一种检测方法,可以将其包含到c++ 17标准库中,以一种比较优雅的方式解决问题。此外,它刚刚被图书馆的基本原理TS v2所接受。它引入了一些元表达式,包括std::is_detect,它可用于在其上面轻松地编写类型或函数检测元表达式。你可以这样使用:
template<typename T>
using toString_t = decltype( std::declval<T&>().toString() );
template<typename T>
constexpr bool has_toString = std::is_detected_v<toString_t, T>;
Note that the example above is untested. The detection toolkit is not available in standard libraries yet but the proposal contains a full implementation that you can easily copy if you really need it. It plays nice with the C++17 feature if constexpr
:
注意,上面的示例没有经过测试。检测工具包在标准库中还没有可用,但是提议包含一个完整的实现,如果您确实需要的话,您可以很容易地复制它。如果constexpr使用c++ 17特性,它会运行得很好:
template<class T>
std::string optionalToString(T* obj)
{
if constexpr (has_toString<T>)
return obj->toString();
else
return "toString not defined";
}
Boost.TTI
Another somewhat idiomatic toolkit to perform such a check - even though less elegant - is Boost.TTI, introduced in Boost 1.54.0. For your example, you would have to use the macro BOOST_TTI_HAS_MEMBER_FUNCTION
. Here is how you could use it:
另一种有点习惯的工具是Boost,尽管它不那么优雅。TTI在Boost 1.54.0中推出。对于您的示例,您必须使用宏BOOST_TTI_HAS_MEMBER_FUNCTION。你可以这样使用:
#include <boost/tti/has_member_function.hpp>
// Generate the metafunction
BOOST_TTI_HAS_MEMBER_FUNCTION(toString)
// Check whether T has a member function toString
// which takes no parameter and returns a std::string
constexpr bool foo = has_member_function_toString<T, std::string>::value;
Then, you could use the bool
to create a SFINAE check.
然后,您可以使用bool创建一个SFINAE检查。
Explanation
解释
The macro BOOST_TTI_HAS_MEMBER_FUNCTION
generates the metafunction has_member_function_toString
which takes the checked type as its first template parameter. The second template parameter corresponds to the return type of the member function, and the following parameters correspond to the types of the function's parameters. The member value
contains true
if the class T
has a member function std::string toString()
.
宏BOOST_TTI_HAS_MEMBER_FUNCTION生成一个metafunction has_member_function_toString,它将检查类型作为第一个模板参数。第二个模板参数对应于成员函数的返回类型,下面的参数对应于函数参数的类型。如果类T有一个成员函数std::string toString(),则成员值包含true。
Alternatively, has_member_function_toString
can take a member function pointer as a template parameter. Therefore, it is possible to replace has_member_function_toString<T, std::string>::value
by has_member_function_toString<std::string T::* ()>::value
.
另外,has_member_function_toString可以将成员函数指针作为模板参数。因此,可以替换has_member_function_toString
#6
28
This is what type traits are there for. Unfortunately, they have to be defined manually. In your case, imagine the following:
这就是特征的类型。不幸的是,它们必须手动定义。在你的案例中,想象一下:
template <typename T>
struct response_trait {
static bool const has_tostring = false;
};
template <>
struct response_trait<your_type_with_tostring> {
static bool const has_tostring = true;
}
#7
22
A simple solution for C++11:
c++ 11的一个简单解决方案:
template<class T>
auto optionalToString(T* obj)
-> decltype( obj->toString() )
{
return obj->toString();
}
auto optionalToString(...) -> string
{
return "toString not defined";
}
Update, 3 years later: (and this is untested). To test for the existence, I think this will work:
更新,3年后:(这是未经测试的)。为了检验是否存在,我认为这是可行的:
template<class T>
constexpr auto test_has_toString_method(T* obj)
-> decltype( obj->toString() , std::true_type{} )
{
return obj->toString();
}
constexpr auto test_has_toString_method(...) -> std::false_type
{
return "toString not defined";
}
#8
17
Well, this question has a long list of answers already, but I would like to emphasize the comment from Morwenn: there is a proposal for C++17 that makes it really much simpler. See N4502 for details, but as a self-contained example consider the following.
这个问题已经有了一长串答案,但我想强调一下莫文恩的评论:有一个关于c++ 17的建议,它让问题变得简单得多。有关详细信息,请参见N4502,但是作为一个自包含的示例,请考虑以下内容。
This part is the constant part, put it in a header.
这部分是常数部分,把它放在header中。
// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf.
template <typename...>
using void_t = void;
// Primary template handles all types not supporting the operation.
template <typename, template <typename> class, typename = void_t<>>
struct detect : std::false_type {};
// Specialization recognizes/validates only types supporting the archetype.
template <typename T, template <typename> class Op>
struct detect<T, Op, void_t<Op<T>>> : std::true_type {};
then there is the variable part, where you specify what you are looking for (a type, a member type, a function, a member function etc.). In the case of the OP:
然后是变量部分,在其中指定要查找的内容(类型、成员类型、函数、成员函数等)。对于OP的情况:
template <typename T>
using toString_t = decltype(std::declval<T>().toString());
template <typename T>
using has_toString = detect<T, toString_t>;
The following example, taken from N4502, shows a more elaborate probe:
下面的例子,取自N4502,展示了一个更复杂的探针:
// Archetypal expression for assignment operation.
template <typename T>
using assign_t = decltype(std::declval<T&>() = std::declval<T const &>())
// Trait corresponding to that archetype.
template <typename T>
using is_assignable = detect<T, assign_t>;
Compared to the other implementations described above, this one is fairly simple: a reduced set of tools (void_t
and detect
) suffices, no need for hairy macros. Besides, it was reported (see N4502) that it is measurably more efficient (compile-time and compiler memory consumption) than previous approaches.
与上面描述的其他实现相比,这个实现相当简单:简化的工具集(void_t和检测)就足够了,不需要复杂的宏。此外,据报道(参见N4502),它比以前的方法更有效(编译时和编译器内存消耗)。
Here is a live example. It works fine with Clang, but unfortunately, GCC versions before 5.1 followed a different interpretation of the C++11 standard which caused void_t
to not work as expected. Yakk already provided the work-around: use the following definition of void_t
(void_t in parameter list works but not as return type):
这里有一个活生生的例子。它在Clang上工作得很好,但不幸的是,5.1之前的GCC版本遵循了不同的c++ 11标准解释,导致void_t不能正常工作。Yakk已经提供了解决方案:使用以下void_t的定义(参数列表中的void_t可以工作,但不作为返回类型):
#if __GNUC__ < 5 && ! defined __clang__
// https://*.com/a/28967049/1353549
template <typename...>
struct voider
{
using type = void;
};
template <typename...Ts>
using void_t = typename voider<Ts...>::type;
#else
template <typename...>
using void_t = void;
#endif
#9
8
Here are some usage snippets: *The guts for all this are farther down
以下是一些用法片段:*所有这些的核心都在下面
Check for member x
in a given class. Could be var, func, class, union, or enum:
在给定的类中检查成员x。可以是var、func、class、union或enum:
CREATE_MEMBER_CHECK(x);
bool has_x = has_member_x<class_to_check_for_x>::value;
Check for member function void x()
:
检查成员函数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;
Check for member variable x
:
检查成员变量x:
CREATE_MEMBER_VAR_CHECK(x);
bool has_var_x = has_member_var_x<class_to_check_for_x>::value;
Check for member class x
:
会员编号x:
CREATE_MEMBER_CLASS_CHECK(x);
bool has_class_x = has_member_class_x<class_to_check_for_x>::value;
Check for member union x
:
会员大会检查:
CREATE_MEMBER_UNION_CHECK(x);
bool has_union_x = has_member_union_x<class_to_check_for_x>::value;
Check for member enum x
:
检查成员enum x:
CREATE_MEMBER_ENUM_CHECK(x);
bool has_enum_x = has_member_enum_x<class_to_check_for_x>::value;
Check for any member function x
regardless of signature:
检查任何成员函数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;
Details and core:
细节和核心:
/*
- 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;
};
Macros (El Diablo!):
宏(El暗黑破坏神!):
CREATE_MEMBER_CHECK:
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:
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:
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:
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:
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:
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:
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_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)
#10
8
This is a C++11 solution for the general problem if "If I did X, would it compile?"
如果“如果我做了X,它会编译吗?”
template<class> struct type_sink { typedef void type; }; // consumes a type, and makes it `void`
template<class T> using type_sink_t = typename type_sink<T>::type;
template<class T, class=void> struct has_to_string : std::false_type {}; \
template<class T> struct has_to_string<
T,
type_sink_t< decltype( std::declval<T>().toString() ) >
>: std::true_type {};
Trait has_to_string
such that has_to_string<T>::value
is true
if and only if T
has a method .toString
that can be invoked with 0 arguments in this context.
Trait has_to_string这样的has_to_string
Next, I'd use tag dispatching:
接下来,我将使用标签分派:
namespace details {
template<class T>
std::string optionalToString_helper(T* obj, std::true_type /*has_to_string*/) {
return obj->toString();
}
template<class T>
std::string optionalToString_helper(T* obj, std::false_type /*has_to_string*/) {
return "toString not defined";
}
}
template<class T>
std::string optionalToString(T* obj) {
return details::optionalToString_helper( obj, has_to_string<T>{} );
}
which tends to be more maintainable than complex SFINAE expressions.
它比复杂的SFINAE表达式更易于维护。
You can write these traits with a macro if you find yourself doing it alot, but they are relatively simple (a few lines each) so maybe not worth it:
如果你发现自己做了很多,你可以用宏来写这些特征,但它们相对简单(每行只有几行),所以可能不值得:
#define MAKE_CODE_TRAIT( TRAIT_NAME, ... ) \
template<class T, class=void> struct TRAIT_NAME : std::false_type {}; \
template<class T> struct TRAIT_NAME< T, type_sink_t< decltype( __VA_ARGS__ ) > >: std::true_type {};
what the above does is create a macro MAKE_CODE_TRAIT
. You pass it the name of the trait you want, and some code that can test the type T
. Thus:
上面所做的是创建一个宏MAKE_CODE_TRAIT。你给它传递你想要的特性的名称,以及一些可以测试类型t的代码。
MAKE_CODE_TRAIT( has_to_string, std::declval<T>().toString() )
creates the above traits class.
创建上述特征类。
As an aside, the above technique is part of what MS calls "expression SFINAE", and their 2013 compiler fails pretty hard.
顺便提一下,上面的技术是MS所称的“expression SFINAE”的一部分,而他们的2013年编译器也很难通过。
Note that in C++1y the following syntax is possible:
注意,在c++ 1y中,可以使用以下语法:
template<class T>
std::string optionalToString(T* obj) {
return compiled_if< has_to_string >(*obj, [&](auto&& obj) {
return obj.toString();
}) *compiled_else ([&]{
return "toString not defined";
});
}
which is an inline compilation conditional branch that abuses lots of C++ features. Doing so is probably not worth it, as the benefit (of code being inline) is not worth the cost (of next to nobody understanding how it works), but the existence of that above solution may be of interest.
这是一个内联编译条件分支,滥用大量c++特性。这样做可能不值得,因为(代码是内联的)好处不值得(几乎没有人知道它是如何工作的),但是上面的解决方案的存在可能值得关注。
#11
6
The standard C++ solution presented here by litb will not work as expected if the method happens to be defined in a base class.
如果方法碰巧在基类中定义,那么litb提供的标准c++解决方案将不会像预期的那样工作。
For a solution that handles this situation refer to :
对于处理这种情况的解决方案,请参考:
In Russian : http://www.rsdn.ru/forum/message/2759773.1.aspx
俄罗斯:http://www.rsdn.ru/forum/message/2759773.1.aspx
English Translation by Roman.Perepelitsa : http://groups.google.com/group/comp.lang.c++.moderated/tree/browse_frm/thread/4f7c7a96f9afbe44/c95a7b4c645e449f?pli=1
罗马的英语翻译。Perepelitsa:http://groups.google.com/group/comp.lang.c + + .moderated /树/ browse_frm /线/ 4 f7c7a96f9afbe44 / c95a7b4c645e449f ? pli = 1
It is insanely clever. However one issue with this solutiion is that gives compiler errors if the type being tested is one that cannot be used as a base class (e.g. primitive types)
这是疯狂的聪明。然而,这个解决方案的一个问题是,如果被测试的类型是不能用作基类(如基元类型)的类型,则会给编译器带来错误。
In Visual Studio, I noticed that if working with method having no arguments, an extra pair of redundant ( ) needs to be inserted around the argments to deduce( ) in the sizeof expression.
在Visual Studio中,我注意到,如果使用没有参数的方法,需要在argments周围插入一对冗余(),以在sizeof表达式中推断()。
#12
6
I wrote an answer to this in another thread that (unlike the solutions above) also checks inherited member functions:
我在另一个线程中写了一个答案(不像上面的解决方案),还检查了继承的成员函数:
SFINAE to check for inherited member functions
检查继承的成员函数
Here are some example from that solution:
下面是这个解决方案中的一些例子:
Example1:
We are checking for a member with the following signature: T::const_iterator begin() const
我们正在检查一个具有以下签名的成员:T::const_iterator begin() const
template<class T> struct has_const_begin
{
typedef char (&Yes)[1];
typedef char (&No)[2];
template<class U>
static Yes test(U const * data,
typename std::enable_if<std::is_same<
typename U::const_iterator,
decltype(data->begin())
>::value>::type * = 0);
static No test(...);
static const bool value = sizeof(Yes) == sizeof(has_const_begin::test((typename std::remove_reference<T>::type*)0));
};
Please notice that it even checks the constness of the method, and works with primitive types, as well. (I mean has_const_begin<int>::value
is false and doesn't cause a compile-time error.)
请注意,它甚至检查了方法的一致性,并使用了原始类型。(我的意思是has_const_begin
Example 2
Now we are looking for the signature: void foo(MyClass&, unsigned)
现在我们正在寻找签名:void foo(MyClass&, unsigned)
template<class T> struct has_foo
{
typedef char (&Yes)[1];
typedef char (&No)[2];
template<class U>
static Yes test(U * data, MyClass* arg1 = 0,
typename std::enable_if<std::is_void<
decltype(data->foo(*arg1, 1u))
>::value>::type * = 0);
static No test(...);
static const bool value = sizeof(Yes) == sizeof(has_foo::test((typename std::remove_reference<T>::type*)0));
};
Please notice that MyClass doesn't has to be default constructible or to satisfy any special concept. The technique works with template members, as well.
请注意,MyClass不必是默认可构造的,也不必满足任何特殊的概念。该技术也适用于模板成员。
I am eagerly waiting opinions regarding this.
我在急切地等待有关这方面的意见。
#13
6
Now this was a nice little puzzle - great question!
这是一个很好的小难题——很好的问题!
Here's an alternative to Nicola Bonelli's solution that does not rely on the non-standard typeof
operator.
这里有一个替代Nicola Bonelli的解决方案,它不依赖于非标准类型运算符。
Unfortunately, it does not work on GCC (MinGW) 3.4.5 or Digital Mars 8.42n, but it does work on all versions of MSVC (including VC6) and on Comeau C++.
不幸的是,它并不适用于GCC (MinGW) 3.4.5或数字火星8.42n,但它确实适用于所有版本的MSVC(包括VC6)和como c++。
The longer comment block has the details on how it works (or is supposed to work). As it says, I'm not sure which behavior is standards compliant - I'd welcome commentary on that.
较长的注释块包含关于它如何工作(或应该如何工作)的细节。正如它所言,我不确定哪种行为符合标准——我欢迎对此的评论。
update - 7 Nov 2008:
更新- 2008年11月7日:
It looks like while this code is syntactically correct, the behavior that MSVC and Comeau C++ show does not follow the standard (thanks to Leon Timmermans and litb for pointing me in the right direction). The C++03 standard says the following:
看起来虽然这段代码在语法上是正确的,但是MSVC和comau c++所显示的行为并没有遵循标准(感谢Leon Timmermans和litb为我指明了正确的方向)。c++ 03标准是这样说的:
14.6.2 Dependent names [temp.dep]
14.6.2依赖名称(temp.dep)
Paragraph 3
段3
In the definition of a class template or a member of a class template, if a base class of the class template depends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member.
定义的一个类模板或类模板的成员,如果基类的类模板取决于一个模板参数,基类也不是期间检查不合格名称查找范围的角度定义类模板或成员或在一个实例化的类模板或成员。
So, it looks like that when MSVC or Comeau consider the toString()
member function of T
performing name lookup at the call site in doToString()
when the template is instantiated, that is incorrect (even though it's actually the behavior I was looking for in this case).
因此,当MSVC或comau考虑在doToString()中执行名称查找的toString()成员函数时,当模板被实例化时,它看起来是不正确的(即使它实际上是我在本例中寻找的行为)。
The behavior of GCC and Digital Mars looks to be correct - in both cases the non-member toString()
function is bound to the call.
GCC和Digital Mars的行为看起来是正确的——在这两种情况下,非成员toString()函数都绑定到调用。
Rats - I thought I might have found a clever solution, instead I uncovered a couple compiler bugs...
老鼠——我想我可能找到了一个聪明的解决方案,但是我发现了一些编译程序错误……
#include <iostream>
#include <string>
struct Hello
{
std::string toString() {
return "Hello";
}
};
struct Generic {};
// the following namespace keeps the toString() method out of
// most everything - except the other stuff in this
// compilation unit
namespace {
std::string toString()
{
return "toString not defined";
}
template <typename T>
class optionalToStringImpl : public T
{
public:
std::string doToString() {
// in theory, the name lookup for this call to
// toString() should find the toString() in
// the base class T if one exists, but if one
// doesn't exist in the base class, it'll
// find the free toString() function in
// the private namespace.
//
// This theory works for MSVC (all versions
// from VC6 to VC9) and Comeau C++, but
// does not work with MinGW 3.4.5 or
// Digital Mars 8.42n
//
// I'm honestly not sure what the standard says
// is the correct behavior here - it's sort
// of like ADL (Argument Dependent Lookup -
// also known as Koenig Lookup) but without
// arguments (except the implied "this" pointer)
return toString();
}
};
}
template <typename T>
std::string optionalToString(T & obj)
{
// ugly, hacky cast...
optionalToStringImpl<T>* temp = reinterpret_cast<optionalToStringImpl<T>*>( &obj);
return temp->doToString();
}
int
main(int argc, char *argv[])
{
Hello helloObj;
Generic genericObj;
std::cout << optionalToString( helloObj) << std::endl;
std::cout << optionalToString( genericObj) << std::endl;
return 0;
}
#14
5
MSVC has the __if_exists and __if_not_exists keywords (Doc). Together with the typeof-SFINAE approach of Nicola I could create a check for GCC and MSVC like the OP looked for.
MSVC有__if_exists和__if_not_exist关键字(Doc)。结合Nicola的sfinae类型方法,我可以为GCC和MSVC创建一个类似OP所查找的检查。
Update: Source can be found Here
更新:源代码可以在这里找到。
#15
4
I modified the solution provided in https://*.com/a/264088/2712152 to make it a bit more general. Also since it doesn't use any of the new C++11 features we can use it with old compilers and should also work with msvc. But the compilers should enable C99 to use this since it uses variadic macros.
我修改了https://*.com/a/264088/2712152中提供的解决方案,使其更通用一些。此外,由于它不使用任何新的c++ 11特性,我们可以对旧的编译器使用它,也应该与msvc一起使用。但是编译器应该允许C99使用它,因为它使用可变宏。
The following macro can be used to check if a particular class has a particular typedef or not.
下面的宏可以用来检查特定的类是否具有特定的类型定义。
/**
* @class : HAS_TYPEDEF
* @brief : This macro will be used to check if a class has a particular
* typedef or not.
* @param typedef_name : Name of Typedef
* @param name : Name of struct which is going to be run the test for
* the given particular typedef specified in typedef_name
*/
#define HAS_TYPEDEF(typedef_name, name) \
template <typename T> \
struct name { \
typedef char yes[1]; \
typedef char no[2]; \
template <typename U> \
struct type_check; \
template <typename _1> \
static yes& chk(type_check<typename _1::typedef_name>*); \
template <typename> \
static no& chk(...); \
static bool const value = sizeof(chk<T>(0)) == sizeof(yes); \
}
The following macro can be used to check if a particular class has a particular member function or not with any given number of arguments.
下面的宏可以用来检查一个特定的类是否有一个特定的成员函数或者没有给定的参数。
/**
* @class : HAS_MEM_FUNC
* @brief : This macro will be used to check if a class has a particular
* member function implemented in the public section or not.
* @param func : Name of Member Function
* @param name : Name of struct which is going to be run the test for
* the given particular member function name specified in func
* @param return_type: Return type of the member function
* @param ellipsis(...) : Since this is macro should provide test case for every
* possible member function we use variadic macros to cover all possibilities
*/
#define HAS_MEM_FUNC(func, name, return_type, ...) \
template <typename T> \
struct name { \
typedef return_type (T::*Sign)(__VA_ARGS__); \
typedef char yes[1]; \
typedef char no[2]; \
template <typename U, U> \
struct type_check; \
template <typename _1> \
static yes& chk(type_check<Sign, &_1::func>*); \
template <typename> \
static no& chk(...); \
static bool const value = sizeof(chk<T>(0)) == sizeof(yes); \
}
We can use the above 2 macros to perform the checks for has_typedef and has_mem_func as:
我们可以使用上面的两个宏来执行has_typedef和has_mem_func的检查:
class A {
public:
typedef int check;
void check_function() {}
};
class B {
public:
void hello(int a, double b) {}
void hello() {}
};
HAS_MEM_FUNC(check_function, has_check_function, void, void);
HAS_MEM_FUNC(hello, hello_check, void, int, double);
HAS_MEM_FUNC(hello, hello_void_check, void, void);
HAS_TYPEDEF(check, has_typedef_check);
int main() {
std::cout << "Check Function A:" << has_check_function<A>::value << std::endl;
std::cout << "Check Function B:" << has_check_function<B>::value << std::endl;
std::cout << "Hello Function A:" << hello_check<A>::value << std::endl;
std::cout << "Hello Function B:" << hello_check<B>::value << std::endl;
std::cout << "Hello void Function A:" << hello_void_check<A>::value << std::endl;
std::cout << "Hello void Function B:" << hello_void_check<B>::value << std::endl;
std::cout << "Check Typedef A:" << has_typedef_check<A>::value << std::endl;
std::cout << "Check Typedef B:" << has_typedef_check<B>::value << std::endl;
}
#16
3
Strange nobody suggested the following nice trick I saw once on this very site :
奇怪的是,没有人建议我在这个网站上看到过下面的好点子:
template <class T>
struct has_foo
{
struct S { void foo(...); };
struct derived : S, T {};
template <typename V, V> struct W {};
template <typename X>
char (&test(W<void (X::*)(), &X::foo> *))[1];
template <typename>
char (&test(...))[2];
static const bool value = sizeof(test<derived>(0)) == 1;
};
You have to make sure T is a class. It seems that ambiguity in the lookup of foo is a substitution failure. I made it work on gcc, not sure if it is standard though.
你必须确保T是一个类。似乎查找foo中的歧义是替换失败。我让它在gcc上工作,但不确定它是否是标准的。
#17
3
The generic template that can be used for checking if some "feature" is supported by the type:
可用于检查类型是否支持某些“特性”的通用模板:
#include <type_traits>
template <template <typename> class TypeChecker, typename Type>
struct is_supported
{
// these structs are used to recognize which version
// of the two functions was chosen during overload resolution
struct supported {};
struct not_supported {};
// this overload of chk will be ignored by SFINAE principle
// if TypeChecker<Type_> is invalid type
template <typename Type_>
static supported chk(typename std::decay<TypeChecker<Type_>>::type *);
// ellipsis has the lowest conversion rank, so this overload will be
// chosen during overload resolution only if the template overload above is ignored
template <typename Type_>
static not_supported chk(...);
// if the template overload of chk is chosen during
// overload resolution then the feature is supported
// if the ellipses overload is chosen the the feature is not supported
static constexpr bool value = std::is_same<decltype(chk<Type>(nullptr)),supported>::value;
};
The template that checks whether there is a method foo
that is compatible with signature double(const char*)
检查是否有与签名double兼容的方法foo的模板(const char*)
// if T doesn't have foo method with the signature that allows to compile the bellow
// expression then instantiating this template is Substitution Failure (SF)
// which Is Not An Error (INAE) if this happens during overload resolution
template <typename T>
using has_foo = decltype(double(std::declval<T>().foo(std::declval<const char*>())));
Examples
例子
// types that support has_foo
struct struct1 { double foo(const char*); }; // exact signature match
struct struct2 { int foo(const std::string &str); }; // compatible signature
struct struct3 { float foo(...); }; // compatible ellipsis signature
struct struct4 { template <typename T>
int foo(T t); }; // compatible template signature
// types that do not support has_foo
struct struct5 { void foo(const char*); }; // returns void
struct struct6 { std::string foo(const char*); }; // std::string can't be converted to double
struct struct7 { double foo( int *); }; // const char* can't be converted to int*
struct struct8 { double bar(const char*); }; // there is no foo method
int main()
{
std::cout << std::boolalpha;
std::cout << is_supported<has_foo, int >::value << std::endl; // false
std::cout << is_supported<has_foo, double >::value << std::endl; // false
std::cout << is_supported<has_foo, struct1>::value << std::endl; // true
std::cout << is_supported<has_foo, struct2>::value << std::endl; // true
std::cout << is_supported<has_foo, struct3>::value << std::endl; // true
std::cout << is_supported<has_foo, struct4>::value << std::endl; // true
std::cout << is_supported<has_foo, struct5>::value << std::endl; // false
std::cout << is_supported<has_foo, struct6>::value << std::endl; // false
std::cout << is_supported<has_foo, struct7>::value << std::endl; // false
std::cout << is_supported<has_foo, struct8>::value << std::endl; // false
return 0;
}
http://coliru.stacked-crooked.com/a/83c6a631ed42cea4
http://coliru.stacked-crooked.com/a/83c6a631ed42cea4
#18
2
There are a lot of answers here, but I failed, to find a version, that performs real method resolution ordering, while not using any of the newer c++ features (only using c++98 features).
Note: This version is tested and working with vc++2013, g++ 5.2.0 and the onlline compiler.
这里有很多答案,但是我没有找到一个版本,它执行真正的方法排序,同时不使用任何新的c++特性(仅使用c++98特性)。注意:这个版本已经测试并使用了vc++2013、g++ 5.2.0和onlline编译器。
So I came up with a version, that only uses sizeof():
所以我想到了一个只使用sizeof()的版本:
template<typename T> T declval(void);
struct fake_void { };
template<typename T> T &operator,(T &,fake_void);
template<typename T> T const &operator,(T const &,fake_void);
template<typename T> T volatile &operator,(T volatile &,fake_void);
template<typename T> T const volatile &operator,(T const volatile &,fake_void);
struct yes { char v[1]; };
struct no { char v[2]; };
template<bool> struct yes_no:yes{};
template<> struct yes_no<false>:no{};
template<typename T>
struct has_awesome_member {
template<typename U> static yes_no<(sizeof((
declval<U>().awesome_member(),fake_void()
))!=0)> check(int);
template<typename> static no check(...);
enum{value=sizeof(check<T>(0)) == sizeof(yes)};
};
struct foo { int awesome_member(void); };
struct bar { };
struct foo_void { void awesome_member(void); };
struct wrong_params { void awesome_member(int); };
static_assert(has_awesome_member<foo>::value,"");
static_assert(!has_awesome_member<bar>::value,"");
static_assert(has_awesome_member<foo_void>::value,"");
static_assert(!has_awesome_member<wrong_params>::value,"");
Live demo (with extended return type checking and vc++2010 workaround): http://cpp.sh/5b2vs
现场演示(带有扩展的返回类型检查和vc++2010解决方案):http://cpp.sh/5b2vs。
No source, as I came up with it myself.
没有来源,因为我自己想到的。
When running the Live demo on the g++ compiler, please note that array sizes of 0 are allowed, meaning that the static_assert used will not trigger a compiler error, even when it fails.
A commonly used work-around is to replace the 'typedef' in the macro with 'extern'.
在g++编译器上运行Live demo时,请注意数组大小为0是允许的,这意味着使用的static_assert即使失败也不会触发编译错误。一种常用的变通方法是将宏中的“typedef”替换为“extern”。
#19
2
An example using SFINAE and template partial specialization, by writing a Has_foo
concept check:
使用SFINAE和模板局部专门化编写Has_foo概念检查的示例:
#include <type_traits>
struct A{};
struct B{ int foo(int a, int b);};
struct C{void foo(int a, int b);};
struct D{int foo();};
struct E: public B{};
// available in C++17 onwards as part of <type_traits>
template<typename...>
using void_t = void;
template<typename T, typename = void> struct Has_foo: std::false_type{};
template<typename T>
struct Has_foo<T, void_t<
std::enable_if_t<
std::is_same<
int,
decltype(std::declval<T>().foo((int)0, (int)0))
>::value
>
>>: std::true_type{};
static_assert(not Has_foo<A>::value, "A does not have a foo");
static_assert(Has_foo<B>::value, "B has a foo");
static_assert(not Has_foo<C>::value, "C has a foo with the wrong return. ");
static_assert(not Has_foo<D>::value, "D has a foo with the wrong arguments. ");
static_assert(Has_foo<E>::value, "E has a foo since it inherits from B");
#20
1
How about this solution?
该解决方案如何?
#include <type_traits>
template <typename U, typename = void> struct hasToString : std::false_type { };
template <typename U>
struct hasToString<U,
typename std::enable_if<bool(sizeof(&U::toString))>::type
> : std::true_type { };
#21
1
Here is my version that handles all possible member function overloads with arbitrary arity, including template member functions, possibly with default arguments. It distinguishes 3 mutually exclusive scenarios when making a member function call to some class type, with given arg types: (1) valid, or (2) ambiguous, or (3) non-viable. Example usage:
下面是我的版本,它处理所有可能的成员函数重载,具有任意的特性,包括模板成员函数,可能使用默认参数。它区分了3个相互排斥的场景,当对某个类类型进行成员函数调用时,给定的arg类型:(1)有效,或(2)不明确,或(3)不可行。使用示例:
#include <string>
#include <vector>
HAS_MEM(bar)
HAS_MEM_FUN_CALL(bar)
struct test
{
void bar(int);
void bar(double);
void bar(int,double);
template < typename T >
typename std::enable_if< not std::is_integral<T>::value >::type
bar(const T&, int=0){}
template < typename T >
typename std::enable_if< std::is_integral<T>::value >::type
bar(const std::vector<T>&, T*){}
template < typename T >
int bar(const std::string&, int){}
};
Now you can use it like this:
现在你可以这样使用它:
int main(int argc, const char * argv[])
{
static_assert( has_mem_bar<test>::value , "");
static_assert( has_valid_mem_fun_call_bar<test(char const*,long)>::value , "");
static_assert( has_valid_mem_fun_call_bar<test(std::string&,long)>::value , "");
static_assert( has_valid_mem_fun_call_bar<test(std::vector<int>, int*)>::value , "");
static_assert( has_no_viable_mem_fun_call_bar<test(std::vector<double>, double*)>::value , "");
static_assert( has_valid_mem_fun_call_bar<test(int)>::value , "");
static_assert( std::is_same<void,result_of_mem_fun_call_bar<test(int)>::type>::value , "");
static_assert( has_valid_mem_fun_call_bar<test(int,double)>::value , "");
static_assert( not has_valid_mem_fun_call_bar<test(int,double,int)>::value , "");
static_assert( not has_ambiguous_mem_fun_call_bar<test(double)>::value , "");
static_assert( has_ambiguous_mem_fun_call_bar<test(unsigned)>::value , "");
static_assert( has_viable_mem_fun_call_bar<test(unsigned)>::value , "");
static_assert( has_viable_mem_fun_call_bar<test(int)>::value , "");
static_assert( has_no_viable_mem_fun_call_bar<test(void)>::value , "");
return 0;
}
Here is the code, written in c++11, however, you can easily port it (with minor tweaks) to non-c++11 that has typeof extensions (e.g. gcc). You can replace the HAS_MEM macro with your own.
这里是用c++11编写的代码,但是,您可以很容易地将它移植到具有扩展类型(例如gcc)的非c++11。您可以用自己的HAS_MEM宏替换HAS_MEM宏。
#pragma once
#if __cplusplus >= 201103
#include <utility>
#include <type_traits>
#define HAS_MEM(mem) \
\
template < typename T > \
struct has_mem_##mem \
{ \
struct yes {}; \
struct no {}; \
\
struct ambiguate_seed { char mem; }; \
template < typename U > struct ambiguate : U, ambiguate_seed {}; \
\
template < typename U, typename = decltype(&U::mem) > static constexpr no test(int); \
template < typename > static constexpr yes test(...); \
\
static bool constexpr value = std::is_same<decltype(test< ambiguate<T> >(0)),yes>::value ; \
typedef std::integral_constant<bool,value> type; \
};
#define HAS_MEM_FUN_CALL(memfun) \
\
template < typename Signature > \
struct has_valid_mem_fun_call_##memfun; \
\
template < typename T, typename... Args > \
struct has_valid_mem_fun_call_##memfun< T(Args...) > \
{ \
struct yes {}; \
struct no {}; \
\
template < typename U, bool = has_mem_##memfun<U>::value > \
struct impl \
{ \
template < typename V, typename = decltype(std::declval<V>().memfun(std::declval<Args>()...)) > \
struct test_result { using type = yes; }; \
\
template < typename V > static constexpr typename test_result<V>::type test(int); \
template < typename > static constexpr no test(...); \
\
static constexpr bool value = std::is_same<decltype(test<U>(0)),yes>::value; \
using type = std::integral_constant<bool, value>; \
}; \
\
template < typename U > \
struct impl<U,false> : std::false_type {}; \
\
static constexpr bool value = impl<T>::value; \
using type = std::integral_constant<bool, value>; \
}; \
\
template < typename Signature > \
struct has_ambiguous_mem_fun_call_##memfun; \
\
template < typename T, typename... Args > \
struct has_ambiguous_mem_fun_call_##memfun< T(Args...) > \
{ \
struct ambiguate_seed { void memfun(...); }; \
\
template < class U, bool = has_mem_##memfun<U>::value > \
struct ambiguate : U, ambiguate_seed \
{ \
using ambiguate_seed::memfun; \
using U::memfun; \
}; \
\
template < class U > \
struct ambiguate<U,false> : ambiguate_seed {}; \
\
static constexpr bool value = not has_valid_mem_fun_call_##memfun< ambiguate<T>(Args...) >::value; \
using type = std::integral_constant<bool, value>; \
}; \
\
template < typename Signature > \
struct has_viable_mem_fun_call_##memfun; \
\
template < typename T, typename... Args > \
struct has_viable_mem_fun_call_##memfun< T(Args...) > \
{ \
static constexpr bool value = has_valid_mem_fun_call_##memfun<T(Args...)>::value \
or has_ambiguous_mem_fun_call_##memfun<T(Args...)>::value; \
using type = std::integral_constant<bool, value>; \
}; \
\
template < typename Signature > \
struct has_no_viable_mem_fun_call_##memfun; \
\
template < typename T, typename... Args > \
struct has_no_viable_mem_fun_call_##memfun < T(Args...) > \
{ \
static constexpr bool value = not has_viable_mem_fun_call_##memfun<T(Args...)>::value; \
using type = std::integral_constant<bool, value>; \
}; \
\
template < typename Signature > \
struct result_of_mem_fun_call_##memfun; \
\
template < typename T, typename... Args > \
struct result_of_mem_fun_call_##memfun< T(Args...) > \
{ \
using type = decltype(std::declval<T>().memfun(std::declval<Args>()...)); \
};
#endif
#22
1
You can skip all the metaprogramming in C++14, and just write this using fit::conditional
from the Fit library:
您可以跳过c++ 14中的所有元编程,只需使用fit::条件句从fit库:
template<class T>
std::string optionalToString(T* x)
{
return fit::conditional(
[](auto* obj) -> decltype(obj->toString()) { return obj->toString(); },
[](auto*) { return "toString not defined"; }
)(x);
}
You can also create the function directly from the lambdas as well:
您也可以直接从lambdas创建函数:
FIT_STATIC_LAMBDA_FUNCTION(optionalToString) = fit::conditional(
[](auto* obj) -> decltype(obj->toString(), std::string()) { return obj->toString(); },
[](auto*) -> std::string { return "toString not defined"; }
);
However, if you are using a compiler that doesn't support generic lambdas, you will have to write separate function objects:
但是,如果您使用的编译器不支持泛型lambdas,则必须编写独立的函数对象:
struct withToString
{
template<class T>
auto operator()(T* obj) const -> decltype(obj->toString(), std::string())
{
return obj->toString();
}
};
struct withoutToString
{
template<class T>
std::string operator()(T*) const
{
return "toString not defined";
}
};
FIT_STATIC_FUNCTION(optionalToString) = fit::conditional(
withToString(),
withoutToString()
);
#23
0
Here is an example of the working code.
下面是一个工作代码的示例。
template<typename T>
using toStringFn = decltype(std::declval<const T>().toString());
template <class T, toStringFn<T>* = nullptr>
std::string optionalToString(const T* obj, int)
{
return obj->toString();
}
template <class T>
std::string optionalToString(const T* obj, long)
{
return "toString not defined";
}
int main()
{
A* a;
B* b;
std::cout << optionalToString(a, 0) << std::endl; // This is A
std::cout << optionalToString(b, 0) << std::endl; // toString not defined
}
toStringFn<T>* = nullptr
will enable the function which takes extra int
argument which has a priority over function which takes long
when called with 0
.
toStringFn
You can use the same principle for the functions which returns true
if function is implemented.
如果实现了函数,则可以对返回true的函数使用相同的原则。
template <typename T>
constexpr bool toStringExists(long)
{
return false;
}
template <typename T, toStringFn<T>* = nullptr>
constexpr bool toStringExists(int)
{
return true;
}
int main()
{
A* a;
B* b;
std::cout << toStringExists<A>(0) << std::endl; // true
std::cout << toStringExists<B>(0) << std::endl; // false
}
#1
276
Yes, with SFINAE you can check if a given class does provide a certain method. Here's the working code:
是的,使用SFINAE您可以检查给定的类是否提供了特定的方法。这里的工作代码:
#include <iostream>
struct Hello
{
int helloworld() { return 0; }
};
struct Generic {};
// SFINAE test
template <typename T>
class has_helloworld
{
typedef char one;
typedef long two;
template <typename C> static one test( typeof(&C::helloworld) ) ;
template <typename C> static two test(...);
public:
enum { value = sizeof(test<T>(0)) == sizeof(char) };
};
int main(int argc, char *argv[])
{
std::cout << has_helloworld<Hello>::value << std::endl;
std::cout << has_helloworld<Generic>::value << std::endl;
return 0;
}
I've just tested it with Linux and gcc 4.1/4.3. I don't know if it's portable to other platforms running different compilers.
我刚刚用Linux和gcc 4.1/4.3测试了它。我不知道它是否可以移植到运行不同编译器的其他平台。
#2
222
This question is old, but with C++11 we got a new way to check for a functions existence (or existence of any non-type member, really), relying on SFINAE again:
这个问题很老了,但是有了c++ 11,我们有了一种新的方法来检查函数的存在性(或者任何非类型成员的存在性,真的),再次依赖于SFINAE:
template<class T>
auto serialize_imp(std::ostream& os, T const& obj, int)
-> decltype(os << obj, void())
{
os << obj;
}
template<class T>
auto serialize_imp(std::ostream& os, T const& obj, long)
-> decltype(obj.stream(os), void())
{
obj.stream(os);
}
template<class T>
auto serialize(std::ostream& os, T const& obj)
-> decltype(serialize_imp(os, obj, 0), void())
{
serialize_imp(os, obj, 0);
}
生活的例子。
Now onto some explanations. First thing, I use expression SFINAE to exclude the serialize(_imp)
functions from overload resolution, if the first expression inside decltype
isn't valid (aka, the function doesn't exist).
现在在一些解释。首先,我使用表达式SFINAE从重载解析中排除序列化(_imp)函数,如果decltype里面的第一个表达式不是有效的(比如,这个函数不存在)。
The void()
is used to make the return type of all those functions void
.
void()用于使所有这些函数的返回类型无效。
The 0
argument is used to prefer the os << obj
overload if both are available (literal 0
is of type int
and as such the first overload is a better match).
如果两者都可用,则使用0参数来选择os << obj重载(文字0是int类型,因此第一个重载是更好的匹配)。
Now, you probably want a trait to check if a function exists. Luckily, it's easy to write that. Note, though, that you need to write a trait yourself for every different function name you might want.
现在,您可能想要一个特性来检查一个函数是否存在。幸运的是,很容易写出来。但是,请注意,您需要为您想要的每个不同的函数名编写一个特性。
#include <type_traits>
template<class>
struct sfinae_true : std::true_type{};
namespace detail{
template<class T, class A0>
static auto test_stream(int)
-> sfinae_true<decltype(std::declval<T>().stream(std::declval<A0>()))>;
template<class, class A0>
static auto test_stream(long) -> std::false_type;
} // detail::
template<class T, class Arg>
struct has_stream : decltype(detail::test_stream<T, Arg>(0)){};
生活的例子。
And on to explanations. First, sfinae_true
is a helper type, and it basically amounts to the same as writing decltype(void(std::declval<T>().stream(a0)), std::true_type{})
. The advantage is simply that it's shorter.
Next, the struct has_stream : decltype(...)
inherits from either std::true_type
or std::false_type
in the end, depending on whether the decltype
check in test_stream
fails or not.
Last, std::declval
gives you a "value" of whatever type you pass, without you needing to know how you can construct it. Note that this is only possible inside an unevaluated context, such as decltype
, sizeof
and others.
和解释。首先,sfinae_true是一个助手类型,它基本上等同于编写decltype(void(std::declval
Note that decltype
is not necessarily needed, as sizeof
(and all unevaluated contexts) got that enhancement. It's just that decltype
already delivers a type and as such is just cleaner. Here's a sizeof
version of one of the overloads:
请注意,并不一定需要decltype,因为sizeof(以及所有未被评估的上下文)得到了这种增强。仅仅是解密已经提供了一种类型,而且这是更清洁的。以下是其中一个重载的sizeof版本:
template<class T>
void serialize_imp(std::ostream& os, T const& obj, int,
int(*)[sizeof((os << obj),0)] = 0)
{
os << obj;
}
生活的例子。
The int
and long
parameters are still there for the same reason. The array pointer is used to provide a context where sizeof
can be used.
由于相同的原因,int和long参数仍然存在。数组指针用于提供可以使用sizeof的上下文。
#3
153
C++ allows SFINAE to be used for this (notice that with C++11 features this is simplier because it supports extended SFINAE on nearly arbitrary expressions - the below was crafted to work with common C++03 compilers):
c++允许为此使用SFINAE(注意,对于c++ 11的特性,这很简单,因为它支持几乎任意表达式上的扩展SFINAE——下面的代码是为与普通c++ 03编译器一起工作而编写的):
#define HAS_MEM_FUNC(func, name) \
template<typename T, typename Sign> \
struct name { \
typedef char yes[1]; \
typedef char no [2]; \
template <typename U, U> struct type_check; \
template <typename _1> static yes &chk(type_check<Sign, &_1::func > *); \
template <typename > static no &chk(...); \
static bool const value = sizeof(chk<T>(0)) == sizeof(yes); \
}
the above template and macro tries to instantiate a template, giving it a member function pointer type, and the actual member function pointer. If the types to not fit, SFINAE causes the template to be ignored. Usage like this:
上面的模板和宏尝试实例化一个模板,给它一个成员函数指针类型,以及实际的成员函数指针。如果类型不适合,SFINAE将导致模板被忽略。用法如下:
HAS_MEM_FUNC(toString, has_to_string);
template<typename T> void
doSomething() {
if(has_to_string<T, std::string(T::*)()>::value) {
...
} else {
...
}
}
But note that you cannot just call that toString
function in that if branch. since the compiler will check for validity in both branches, that would fail for cases the function doesnt exist. One way is to use SFINAE once again (enable_if can be gotten from boost too):
但是注意,不能只在if分支中调用toString函数。由于编译器将检查两个分支中的有效性,因此在函数不存在的情况下会失败。一种方法是再次使用SFINAE (enable_if也可以从boost获得):
template<bool C, typename T = void>
struct enable_if {
typedef T type;
};
template<typename T>
struct enable_if<false, T> { };
HAS_MEM_FUNC(toString, has_to_string);
template<typename T>
typename enable_if<has_to_string<T,
std::string(T::*)()>::value, std::string>::type
doSomething(T * t) {
/* something when T has toString ... */
return t->toString();
}
template<typename T>
typename enable_if<!has_to_string<T,
std::string(T::*)()>::value, std::string>::type
doSomething(T * t) {
/* something when T doesnt have toString ... */
return "T::toString() does not exist.";
}
Have fun using it. The advantage of it is that it also works for overloaded member functions, and also for const member functions (remember using std::string(T::*)() const
as the member function pointer type then!).
玩得开心使用它。它的优点是它也适用于重载的成员函数,也适用于const成员函数(请记住使用std::string(T::*) const作为成员函数指针类型!)
#4
52
Though this question is two years old, I'll dare to add my answer. Hopefully it will clarify the previous, indisputably excellent, solution. I took the very helpful answers of Nicola Bonelli and Johannes Schaub and merged them into a solution that is, IMHO, more readable, clear and does not require the typeof
extension:
虽然这个问题已经有两年了,但我敢补充我的答案。希望它能澄清之前无可争议的优秀解决方案。我把Nicola Bonelli和Johannes Schaub的非常有帮助的答案合并到一个解决方案中,IMHO,更加可读,更清晰,不需要扩展类型:
template <class Type>
class TypeHasToString
{
// This type won't compile if the second template parameter isn't of type T,
// so I can put a function pointer type in the first parameter and the function
// itself in the second thus checking that the function has a specific signature.
template <typename T, T> struct TypeCheck;
typedef char Yes;
typedef long No;
// A helper struct to hold the declaration of the function pointer.
// Change it if the function signature changes.
template <typename T> struct ToString
{
typedef void (T::*fptr)();
};
template <typename T> static Yes HasToString(TypeCheck< typename ToString<T>::fptr, &T::toString >*);
template <typename T> static No HasToString(...);
public:
static bool const value = (sizeof(HasToString<Type>(0)) == sizeof(Yes));
};
I checked it with gcc 4.1.2. The credit goes mainly to Nicola Bonelli and Johannes Schaub, so give them a vote up if my answer helps you :)
我用gcc 4.1.2检查了一下。这主要归功于尼古拉·波内利(Nicola Bonelli)和约翰内斯·绍布(Johannes Schaub),所以如果我的回答对你有帮助的话,请给他们投票。
#5
35
Detection toolkit
N4502 proposes a detection tookit for inclusion into the C++17 standard library which can solve the problem in a somewhat elegant manner. Moreover, it just got accepted into the library fundamentals TS v2. It introduces some metafunctions, including std::is_detected
which can be used to easily write type or function detection metafunctions on the top of it. Here is how you could use it:
N4502提出了一种检测方法,可以将其包含到c++ 17标准库中,以一种比较优雅的方式解决问题。此外,它刚刚被图书馆的基本原理TS v2所接受。它引入了一些元表达式,包括std::is_detect,它可用于在其上面轻松地编写类型或函数检测元表达式。你可以这样使用:
template<typename T>
using toString_t = decltype( std::declval<T&>().toString() );
template<typename T>
constexpr bool has_toString = std::is_detected_v<toString_t, T>;
Note that the example above is untested. The detection toolkit is not available in standard libraries yet but the proposal contains a full implementation that you can easily copy if you really need it. It plays nice with the C++17 feature if constexpr
:
注意,上面的示例没有经过测试。检测工具包在标准库中还没有可用,但是提议包含一个完整的实现,如果您确实需要的话,您可以很容易地复制它。如果constexpr使用c++ 17特性,它会运行得很好:
template<class T>
std::string optionalToString(T* obj)
{
if constexpr (has_toString<T>)
return obj->toString();
else
return "toString not defined";
}
Boost.TTI
Another somewhat idiomatic toolkit to perform such a check - even though less elegant - is Boost.TTI, introduced in Boost 1.54.0. For your example, you would have to use the macro BOOST_TTI_HAS_MEMBER_FUNCTION
. Here is how you could use it:
另一种有点习惯的工具是Boost,尽管它不那么优雅。TTI在Boost 1.54.0中推出。对于您的示例,您必须使用宏BOOST_TTI_HAS_MEMBER_FUNCTION。你可以这样使用:
#include <boost/tti/has_member_function.hpp>
// Generate the metafunction
BOOST_TTI_HAS_MEMBER_FUNCTION(toString)
// Check whether T has a member function toString
// which takes no parameter and returns a std::string
constexpr bool foo = has_member_function_toString<T, std::string>::value;
Then, you could use the bool
to create a SFINAE check.
然后,您可以使用bool创建一个SFINAE检查。
Explanation
解释
The macro BOOST_TTI_HAS_MEMBER_FUNCTION
generates the metafunction has_member_function_toString
which takes the checked type as its first template parameter. The second template parameter corresponds to the return type of the member function, and the following parameters correspond to the types of the function's parameters. The member value
contains true
if the class T
has a member function std::string toString()
.
宏BOOST_TTI_HAS_MEMBER_FUNCTION生成一个metafunction has_member_function_toString,它将检查类型作为第一个模板参数。第二个模板参数对应于成员函数的返回类型,下面的参数对应于函数参数的类型。如果类T有一个成员函数std::string toString(),则成员值包含true。
Alternatively, has_member_function_toString
can take a member function pointer as a template parameter. Therefore, it is possible to replace has_member_function_toString<T, std::string>::value
by has_member_function_toString<std::string T::* ()>::value
.
另外,has_member_function_toString可以将成员函数指针作为模板参数。因此,可以替换has_member_function_toString
#6
28
This is what type traits are there for. Unfortunately, they have to be defined manually. In your case, imagine the following:
这就是特征的类型。不幸的是,它们必须手动定义。在你的案例中,想象一下:
template <typename T>
struct response_trait {
static bool const has_tostring = false;
};
template <>
struct response_trait<your_type_with_tostring> {
static bool const has_tostring = true;
}
#7
22
A simple solution for C++11:
c++ 11的一个简单解决方案:
template<class T>
auto optionalToString(T* obj)
-> decltype( obj->toString() )
{
return obj->toString();
}
auto optionalToString(...) -> string
{
return "toString not defined";
}
Update, 3 years later: (and this is untested). To test for the existence, I think this will work:
更新,3年后:(这是未经测试的)。为了检验是否存在,我认为这是可行的:
template<class T>
constexpr auto test_has_toString_method(T* obj)
-> decltype( obj->toString() , std::true_type{} )
{
return obj->toString();
}
constexpr auto test_has_toString_method(...) -> std::false_type
{
return "toString not defined";
}
#8
17
Well, this question has a long list of answers already, but I would like to emphasize the comment from Morwenn: there is a proposal for C++17 that makes it really much simpler. See N4502 for details, but as a self-contained example consider the following.
这个问题已经有了一长串答案,但我想强调一下莫文恩的评论:有一个关于c++ 17的建议,它让问题变得简单得多。有关详细信息,请参见N4502,但是作为一个自包含的示例,请考虑以下内容。
This part is the constant part, put it in a header.
这部分是常数部分,把它放在header中。
// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf.
template <typename...>
using void_t = void;
// Primary template handles all types not supporting the operation.
template <typename, template <typename> class, typename = void_t<>>
struct detect : std::false_type {};
// Specialization recognizes/validates only types supporting the archetype.
template <typename T, template <typename> class Op>
struct detect<T, Op, void_t<Op<T>>> : std::true_type {};
then there is the variable part, where you specify what you are looking for (a type, a member type, a function, a member function etc.). In the case of the OP:
然后是变量部分,在其中指定要查找的内容(类型、成员类型、函数、成员函数等)。对于OP的情况:
template <typename T>
using toString_t = decltype(std::declval<T>().toString());
template <typename T>
using has_toString = detect<T, toString_t>;
The following example, taken from N4502, shows a more elaborate probe:
下面的例子,取自N4502,展示了一个更复杂的探针:
// Archetypal expression for assignment operation.
template <typename T>
using assign_t = decltype(std::declval<T&>() = std::declval<T const &>())
// Trait corresponding to that archetype.
template <typename T>
using is_assignable = detect<T, assign_t>;
Compared to the other implementations described above, this one is fairly simple: a reduced set of tools (void_t
and detect
) suffices, no need for hairy macros. Besides, it was reported (see N4502) that it is measurably more efficient (compile-time and compiler memory consumption) than previous approaches.
与上面描述的其他实现相比,这个实现相当简单:简化的工具集(void_t和检测)就足够了,不需要复杂的宏。此外,据报道(参见N4502),它比以前的方法更有效(编译时和编译器内存消耗)。
Here is a live example. It works fine with Clang, but unfortunately, GCC versions before 5.1 followed a different interpretation of the C++11 standard which caused void_t
to not work as expected. Yakk already provided the work-around: use the following definition of void_t
(void_t in parameter list works but not as return type):
这里有一个活生生的例子。它在Clang上工作得很好,但不幸的是,5.1之前的GCC版本遵循了不同的c++ 11标准解释,导致void_t不能正常工作。Yakk已经提供了解决方案:使用以下void_t的定义(参数列表中的void_t可以工作,但不作为返回类型):
#if __GNUC__ < 5 && ! defined __clang__
// https://*.com/a/28967049/1353549
template <typename...>
struct voider
{
using type = void;
};
template <typename...Ts>
using void_t = typename voider<Ts...>::type;
#else
template <typename...>
using void_t = void;
#endif
#9
8
Here are some usage snippets: *The guts for all this are farther down
以下是一些用法片段:*所有这些的核心都在下面
Check for member x
in a given class. Could be var, func, class, union, or enum:
在给定的类中检查成员x。可以是var、func、class、union或enum:
CREATE_MEMBER_CHECK(x);
bool has_x = has_member_x<class_to_check_for_x>::value;
Check for member function void x()
:
检查成员函数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;
Check for member variable x
:
检查成员变量x:
CREATE_MEMBER_VAR_CHECK(x);
bool has_var_x = has_member_var_x<class_to_check_for_x>::value;
Check for member class x
:
会员编号x:
CREATE_MEMBER_CLASS_CHECK(x);
bool has_class_x = has_member_class_x<class_to_check_for_x>::value;
Check for member union x
:
会员大会检查:
CREATE_MEMBER_UNION_CHECK(x);
bool has_union_x = has_member_union_x<class_to_check_for_x>::value;
Check for member enum x
:
检查成员enum x:
CREATE_MEMBER_ENUM_CHECK(x);
bool has_enum_x = has_member_enum_x<class_to_check_for_x>::value;
Check for any member function x
regardless of signature:
检查任何成员函数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;
Details and core:
细节和核心:
/*
- 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;
};
Macros (El Diablo!):
宏(El暗黑破坏神!):
CREATE_MEMBER_CHECK:
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:
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:
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:
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:
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:
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:
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_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)
#10
8
This is a C++11 solution for the general problem if "If I did X, would it compile?"
如果“如果我做了X,它会编译吗?”
template<class> struct type_sink { typedef void type; }; // consumes a type, and makes it `void`
template<class T> using type_sink_t = typename type_sink<T>::type;
template<class T, class=void> struct has_to_string : std::false_type {}; \
template<class T> struct has_to_string<
T,
type_sink_t< decltype( std::declval<T>().toString() ) >
>: std::true_type {};
Trait has_to_string
such that has_to_string<T>::value
is true
if and only if T
has a method .toString
that can be invoked with 0 arguments in this context.
Trait has_to_string这样的has_to_string
Next, I'd use tag dispatching:
接下来,我将使用标签分派:
namespace details {
template<class T>
std::string optionalToString_helper(T* obj, std::true_type /*has_to_string*/) {
return obj->toString();
}
template<class T>
std::string optionalToString_helper(T* obj, std::false_type /*has_to_string*/) {
return "toString not defined";
}
}
template<class T>
std::string optionalToString(T* obj) {
return details::optionalToString_helper( obj, has_to_string<T>{} );
}
which tends to be more maintainable than complex SFINAE expressions.
它比复杂的SFINAE表达式更易于维护。
You can write these traits with a macro if you find yourself doing it alot, but they are relatively simple (a few lines each) so maybe not worth it:
如果你发现自己做了很多,你可以用宏来写这些特征,但它们相对简单(每行只有几行),所以可能不值得:
#define MAKE_CODE_TRAIT( TRAIT_NAME, ... ) \
template<class T, class=void> struct TRAIT_NAME : std::false_type {}; \
template<class T> struct TRAIT_NAME< T, type_sink_t< decltype( __VA_ARGS__ ) > >: std::true_type {};
what the above does is create a macro MAKE_CODE_TRAIT
. You pass it the name of the trait you want, and some code that can test the type T
. Thus:
上面所做的是创建一个宏MAKE_CODE_TRAIT。你给它传递你想要的特性的名称,以及一些可以测试类型t的代码。
MAKE_CODE_TRAIT( has_to_string, std::declval<T>().toString() )
creates the above traits class.
创建上述特征类。
As an aside, the above technique is part of what MS calls "expression SFINAE", and their 2013 compiler fails pretty hard.
顺便提一下,上面的技术是MS所称的“expression SFINAE”的一部分,而他们的2013年编译器也很难通过。
Note that in C++1y the following syntax is possible:
注意,在c++ 1y中,可以使用以下语法:
template<class T>
std::string optionalToString(T* obj) {
return compiled_if< has_to_string >(*obj, [&](auto&& obj) {
return obj.toString();
}) *compiled_else ([&]{
return "toString not defined";
});
}
which is an inline compilation conditional branch that abuses lots of C++ features. Doing so is probably not worth it, as the benefit (of code being inline) is not worth the cost (of next to nobody understanding how it works), but the existence of that above solution may be of interest.
这是一个内联编译条件分支,滥用大量c++特性。这样做可能不值得,因为(代码是内联的)好处不值得(几乎没有人知道它是如何工作的),但是上面的解决方案的存在可能值得关注。
#11
6
The standard C++ solution presented here by litb will not work as expected if the method happens to be defined in a base class.
如果方法碰巧在基类中定义,那么litb提供的标准c++解决方案将不会像预期的那样工作。
For a solution that handles this situation refer to :
对于处理这种情况的解决方案,请参考:
In Russian : http://www.rsdn.ru/forum/message/2759773.1.aspx
俄罗斯:http://www.rsdn.ru/forum/message/2759773.1.aspx
English Translation by Roman.Perepelitsa : http://groups.google.com/group/comp.lang.c++.moderated/tree/browse_frm/thread/4f7c7a96f9afbe44/c95a7b4c645e449f?pli=1
罗马的英语翻译。Perepelitsa:http://groups.google.com/group/comp.lang.c + + .moderated /树/ browse_frm /线/ 4 f7c7a96f9afbe44 / c95a7b4c645e449f ? pli = 1
It is insanely clever. However one issue with this solutiion is that gives compiler errors if the type being tested is one that cannot be used as a base class (e.g. primitive types)
这是疯狂的聪明。然而,这个解决方案的一个问题是,如果被测试的类型是不能用作基类(如基元类型)的类型,则会给编译器带来错误。
In Visual Studio, I noticed that if working with method having no arguments, an extra pair of redundant ( ) needs to be inserted around the argments to deduce( ) in the sizeof expression.
在Visual Studio中,我注意到,如果使用没有参数的方法,需要在argments周围插入一对冗余(),以在sizeof表达式中推断()。
#12
6
I wrote an answer to this in another thread that (unlike the solutions above) also checks inherited member functions:
我在另一个线程中写了一个答案(不像上面的解决方案),还检查了继承的成员函数:
SFINAE to check for inherited member functions
检查继承的成员函数
Here are some example from that solution:
下面是这个解决方案中的一些例子:
Example1:
We are checking for a member with the following signature: T::const_iterator begin() const
我们正在检查一个具有以下签名的成员:T::const_iterator begin() const
template<class T> struct has_const_begin
{
typedef char (&Yes)[1];
typedef char (&No)[2];
template<class U>
static Yes test(U const * data,
typename std::enable_if<std::is_same<
typename U::const_iterator,
decltype(data->begin())
>::value>::type * = 0);
static No test(...);
static const bool value = sizeof(Yes) == sizeof(has_const_begin::test((typename std::remove_reference<T>::type*)0));
};
Please notice that it even checks the constness of the method, and works with primitive types, as well. (I mean has_const_begin<int>::value
is false and doesn't cause a compile-time error.)
请注意,它甚至检查了方法的一致性,并使用了原始类型。(我的意思是has_const_begin
Example 2
Now we are looking for the signature: void foo(MyClass&, unsigned)
现在我们正在寻找签名:void foo(MyClass&, unsigned)
template<class T> struct has_foo
{
typedef char (&Yes)[1];
typedef char (&No)[2];
template<class U>
static Yes test(U * data, MyClass* arg1 = 0,
typename std::enable_if<std::is_void<
decltype(data->foo(*arg1, 1u))
>::value>::type * = 0);
static No test(...);
static const bool value = sizeof(Yes) == sizeof(has_foo::test((typename std::remove_reference<T>::type*)0));
};
Please notice that MyClass doesn't has to be default constructible or to satisfy any special concept. The technique works with template members, as well.
请注意,MyClass不必是默认可构造的,也不必满足任何特殊的概念。该技术也适用于模板成员。
I am eagerly waiting opinions regarding this.
我在急切地等待有关这方面的意见。
#13
6
Now this was a nice little puzzle - great question!
这是一个很好的小难题——很好的问题!
Here's an alternative to Nicola Bonelli's solution that does not rely on the non-standard typeof
operator.
这里有一个替代Nicola Bonelli的解决方案,它不依赖于非标准类型运算符。
Unfortunately, it does not work on GCC (MinGW) 3.4.5 or Digital Mars 8.42n, but it does work on all versions of MSVC (including VC6) and on Comeau C++.
不幸的是,它并不适用于GCC (MinGW) 3.4.5或数字火星8.42n,但它确实适用于所有版本的MSVC(包括VC6)和como c++。
The longer comment block has the details on how it works (or is supposed to work). As it says, I'm not sure which behavior is standards compliant - I'd welcome commentary on that.
较长的注释块包含关于它如何工作(或应该如何工作)的细节。正如它所言,我不确定哪种行为符合标准——我欢迎对此的评论。
update - 7 Nov 2008:
更新- 2008年11月7日:
It looks like while this code is syntactically correct, the behavior that MSVC and Comeau C++ show does not follow the standard (thanks to Leon Timmermans and litb for pointing me in the right direction). The C++03 standard says the following:
看起来虽然这段代码在语法上是正确的,但是MSVC和comau c++所显示的行为并没有遵循标准(感谢Leon Timmermans和litb为我指明了正确的方向)。c++ 03标准是这样说的:
14.6.2 Dependent names [temp.dep]
14.6.2依赖名称(temp.dep)
Paragraph 3
段3
In the definition of a class template or a member of a class template, if a base class of the class template depends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member.
定义的一个类模板或类模板的成员,如果基类的类模板取决于一个模板参数,基类也不是期间检查不合格名称查找范围的角度定义类模板或成员或在一个实例化的类模板或成员。
So, it looks like that when MSVC or Comeau consider the toString()
member function of T
performing name lookup at the call site in doToString()
when the template is instantiated, that is incorrect (even though it's actually the behavior I was looking for in this case).
因此,当MSVC或comau考虑在doToString()中执行名称查找的toString()成员函数时,当模板被实例化时,它看起来是不正确的(即使它实际上是我在本例中寻找的行为)。
The behavior of GCC and Digital Mars looks to be correct - in both cases the non-member toString()
function is bound to the call.
GCC和Digital Mars的行为看起来是正确的——在这两种情况下,非成员toString()函数都绑定到调用。
Rats - I thought I might have found a clever solution, instead I uncovered a couple compiler bugs...
老鼠——我想我可能找到了一个聪明的解决方案,但是我发现了一些编译程序错误……
#include <iostream>
#include <string>
struct Hello
{
std::string toString() {
return "Hello";
}
};
struct Generic {};
// the following namespace keeps the toString() method out of
// most everything - except the other stuff in this
// compilation unit
namespace {
std::string toString()
{
return "toString not defined";
}
template <typename T>
class optionalToStringImpl : public T
{
public:
std::string doToString() {
// in theory, the name lookup for this call to
// toString() should find the toString() in
// the base class T if one exists, but if one
// doesn't exist in the base class, it'll
// find the free toString() function in
// the private namespace.
//
// This theory works for MSVC (all versions
// from VC6 to VC9) and Comeau C++, but
// does not work with MinGW 3.4.5 or
// Digital Mars 8.42n
//
// I'm honestly not sure what the standard says
// is the correct behavior here - it's sort
// of like ADL (Argument Dependent Lookup -
// also known as Koenig Lookup) but without
// arguments (except the implied "this" pointer)
return toString();
}
};
}
template <typename T>
std::string optionalToString(T & obj)
{
// ugly, hacky cast...
optionalToStringImpl<T>* temp = reinterpret_cast<optionalToStringImpl<T>*>( &obj);
return temp->doToString();
}
int
main(int argc, char *argv[])
{
Hello helloObj;
Generic genericObj;
std::cout << optionalToString( helloObj) << std::endl;
std::cout << optionalToString( genericObj) << std::endl;
return 0;
}
#14
5
MSVC has the __if_exists and __if_not_exists keywords (Doc). Together with the typeof-SFINAE approach of Nicola I could create a check for GCC and MSVC like the OP looked for.
MSVC有__if_exists和__if_not_exist关键字(Doc)。结合Nicola的sfinae类型方法,我可以为GCC和MSVC创建一个类似OP所查找的检查。
Update: Source can be found Here
更新:源代码可以在这里找到。
#15
4
I modified the solution provided in https://*.com/a/264088/2712152 to make it a bit more general. Also since it doesn't use any of the new C++11 features we can use it with old compilers and should also work with msvc. But the compilers should enable C99 to use this since it uses variadic macros.
我修改了https://*.com/a/264088/2712152中提供的解决方案,使其更通用一些。此外,由于它不使用任何新的c++ 11特性,我们可以对旧的编译器使用它,也应该与msvc一起使用。但是编译器应该允许C99使用它,因为它使用可变宏。
The following macro can be used to check if a particular class has a particular typedef or not.
下面的宏可以用来检查特定的类是否具有特定的类型定义。
/**
* @class : HAS_TYPEDEF
* @brief : This macro will be used to check if a class has a particular
* typedef or not.
* @param typedef_name : Name of Typedef
* @param name : Name of struct which is going to be run the test for
* the given particular typedef specified in typedef_name
*/
#define HAS_TYPEDEF(typedef_name, name) \
template <typename T> \
struct name { \
typedef char yes[1]; \
typedef char no[2]; \
template <typename U> \
struct type_check; \
template <typename _1> \
static yes& chk(type_check<typename _1::typedef_name>*); \
template <typename> \
static no& chk(...); \
static bool const value = sizeof(chk<T>(0)) == sizeof(yes); \
}
The following macro can be used to check if a particular class has a particular member function or not with any given number of arguments.
下面的宏可以用来检查一个特定的类是否有一个特定的成员函数或者没有给定的参数。
/**
* @class : HAS_MEM_FUNC
* @brief : This macro will be used to check if a class has a particular
* member function implemented in the public section or not.
* @param func : Name of Member Function
* @param name : Name of struct which is going to be run the test for
* the given particular member function name specified in func
* @param return_type: Return type of the member function
* @param ellipsis(...) : Since this is macro should provide test case for every
* possible member function we use variadic macros to cover all possibilities
*/
#define HAS_MEM_FUNC(func, name, return_type, ...) \
template <typename T> \
struct name { \
typedef return_type (T::*Sign)(__VA_ARGS__); \
typedef char yes[1]; \
typedef char no[2]; \
template <typename U, U> \
struct type_check; \
template <typename _1> \
static yes& chk(type_check<Sign, &_1::func>*); \
template <typename> \
static no& chk(...); \
static bool const value = sizeof(chk<T>(0)) == sizeof(yes); \
}
We can use the above 2 macros to perform the checks for has_typedef and has_mem_func as:
我们可以使用上面的两个宏来执行has_typedef和has_mem_func的检查:
class A {
public:
typedef int check;
void check_function() {}
};
class B {
public:
void hello(int a, double b) {}
void hello() {}
};
HAS_MEM_FUNC(check_function, has_check_function, void, void);
HAS_MEM_FUNC(hello, hello_check, void, int, double);
HAS_MEM_FUNC(hello, hello_void_check, void, void);
HAS_TYPEDEF(check, has_typedef_check);
int main() {
std::cout << "Check Function A:" << has_check_function<A>::value << std::endl;
std::cout << "Check Function B:" << has_check_function<B>::value << std::endl;
std::cout << "Hello Function A:" << hello_check<A>::value << std::endl;
std::cout << "Hello Function B:" << hello_check<B>::value << std::endl;
std::cout << "Hello void Function A:" << hello_void_check<A>::value << std::endl;
std::cout << "Hello void Function B:" << hello_void_check<B>::value << std::endl;
std::cout << "Check Typedef A:" << has_typedef_check<A>::value << std::endl;
std::cout << "Check Typedef B:" << has_typedef_check<B>::value << std::endl;
}
#16
3
Strange nobody suggested the following nice trick I saw once on this very site :
奇怪的是,没有人建议我在这个网站上看到过下面的好点子:
template <class T>
struct has_foo
{
struct S { void foo(...); };
struct derived : S, T {};
template <typename V, V> struct W {};
template <typename X>
char (&test(W<void (X::*)(), &X::foo> *))[1];
template <typename>
char (&test(...))[2];
static const bool value = sizeof(test<derived>(0)) == 1;
};
You have to make sure T is a class. It seems that ambiguity in the lookup of foo is a substitution failure. I made it work on gcc, not sure if it is standard though.
你必须确保T是一个类。似乎查找foo中的歧义是替换失败。我让它在gcc上工作,但不确定它是否是标准的。
#17
3
The generic template that can be used for checking if some "feature" is supported by the type:
可用于检查类型是否支持某些“特性”的通用模板:
#include <type_traits>
template <template <typename> class TypeChecker, typename Type>
struct is_supported
{
// these structs are used to recognize which version
// of the two functions was chosen during overload resolution
struct supported {};
struct not_supported {};
// this overload of chk will be ignored by SFINAE principle
// if TypeChecker<Type_> is invalid type
template <typename Type_>
static supported chk(typename std::decay<TypeChecker<Type_>>::type *);
// ellipsis has the lowest conversion rank, so this overload will be
// chosen during overload resolution only if the template overload above is ignored
template <typename Type_>
static not_supported chk(...);
// if the template overload of chk is chosen during
// overload resolution then the feature is supported
// if the ellipses overload is chosen the the feature is not supported
static constexpr bool value = std::is_same<decltype(chk<Type>(nullptr)),supported>::value;
};
The template that checks whether there is a method foo
that is compatible with signature double(const char*)
检查是否有与签名double兼容的方法foo的模板(const char*)
// if T doesn't have foo method with the signature that allows to compile the bellow
// expression then instantiating this template is Substitution Failure (SF)
// which Is Not An Error (INAE) if this happens during overload resolution
template <typename T>
using has_foo = decltype(double(std::declval<T>().foo(std::declval<const char*>())));
Examples
例子
// types that support has_foo
struct struct1 { double foo(const char*); }; // exact signature match
struct struct2 { int foo(const std::string &str); }; // compatible signature
struct struct3 { float foo(...); }; // compatible ellipsis signature
struct struct4 { template <typename T>
int foo(T t); }; // compatible template signature
// types that do not support has_foo
struct struct5 { void foo(const char*); }; // returns void
struct struct6 { std::string foo(const char*); }; // std::string can't be converted to double
struct struct7 { double foo( int *); }; // const char* can't be converted to int*
struct struct8 { double bar(const char*); }; // there is no foo method
int main()
{
std::cout << std::boolalpha;
std::cout << is_supported<has_foo, int >::value << std::endl; // false
std::cout << is_supported<has_foo, double >::value << std::endl; // false
std::cout << is_supported<has_foo, struct1>::value << std::endl; // true
std::cout << is_supported<has_foo, struct2>::value << std::endl; // true
std::cout << is_supported<has_foo, struct3>::value << std::endl; // true
std::cout << is_supported<has_foo, struct4>::value << std::endl; // true
std::cout << is_supported<has_foo, struct5>::value << std::endl; // false
std::cout << is_supported<has_foo, struct6>::value << std::endl; // false
std::cout << is_supported<has_foo, struct7>::value << std::endl; // false
std::cout << is_supported<has_foo, struct8>::value << std::endl; // false
return 0;
}
http://coliru.stacked-crooked.com/a/83c6a631ed42cea4
http://coliru.stacked-crooked.com/a/83c6a631ed42cea4
#18
2
There are a lot of answers here, but I failed, to find a version, that performs real method resolution ordering, while not using any of the newer c++ features (only using c++98 features).
Note: This version is tested and working with vc++2013, g++ 5.2.0 and the onlline compiler.
这里有很多答案,但是我没有找到一个版本,它执行真正的方法排序,同时不使用任何新的c++特性(仅使用c++98特性)。注意:这个版本已经测试并使用了vc++2013、g++ 5.2.0和onlline编译器。
So I came up with a version, that only uses sizeof():
所以我想到了一个只使用sizeof()的版本:
template<typename T> T declval(void);
struct fake_void { };
template<typename T> T &operator,(T &,fake_void);
template<typename T> T const &operator,(T const &,fake_void);
template<typename T> T volatile &operator,(T volatile &,fake_void);
template<typename T> T const volatile &operator,(T const volatile &,fake_void);
struct yes { char v[1]; };
struct no { char v[2]; };
template<bool> struct yes_no:yes{};
template<> struct yes_no<false>:no{};
template<typename T>
struct has_awesome_member {
template<typename U> static yes_no<(sizeof((
declval<U>().awesome_member(),fake_void()
))!=0)> check(int);
template<typename> static no check(...);
enum{value=sizeof(check<T>(0)) == sizeof(yes)};
};
struct foo { int awesome_member(void); };
struct bar { };
struct foo_void { void awesome_member(void); };
struct wrong_params { void awesome_member(int); };
static_assert(has_awesome_member<foo>::value,"");
static_assert(!has_awesome_member<bar>::value,"");
static_assert(has_awesome_member<foo_void>::value,"");
static_assert(!has_awesome_member<wrong_params>::value,"");
Live demo (with extended return type checking and vc++2010 workaround): http://cpp.sh/5b2vs
现场演示(带有扩展的返回类型检查和vc++2010解决方案):http://cpp.sh/5b2vs。
No source, as I came up with it myself.
没有来源,因为我自己想到的。
When running the Live demo on the g++ compiler, please note that array sizes of 0 are allowed, meaning that the static_assert used will not trigger a compiler error, even when it fails.
A commonly used work-around is to replace the 'typedef' in the macro with 'extern'.
在g++编译器上运行Live demo时,请注意数组大小为0是允许的,这意味着使用的static_assert即使失败也不会触发编译错误。一种常用的变通方法是将宏中的“typedef”替换为“extern”。
#19
2
An example using SFINAE and template partial specialization, by writing a Has_foo
concept check:
使用SFINAE和模板局部专门化编写Has_foo概念检查的示例:
#include <type_traits>
struct A{};
struct B{ int foo(int a, int b);};
struct C{void foo(int a, int b);};
struct D{int foo();};
struct E: public B{};
// available in C++17 onwards as part of <type_traits>
template<typename...>
using void_t = void;
template<typename T, typename = void> struct Has_foo: std::false_type{};
template<typename T>
struct Has_foo<T, void_t<
std::enable_if_t<
std::is_same<
int,
decltype(std::declval<T>().foo((int)0, (int)0))
>::value
>
>>: std::true_type{};
static_assert(not Has_foo<A>::value, "A does not have a foo");
static_assert(Has_foo<B>::value, "B has a foo");
static_assert(not Has_foo<C>::value, "C has a foo with the wrong return. ");
static_assert(not Has_foo<D>::value, "D has a foo with the wrong arguments. ");
static_assert(Has_foo<E>::value, "E has a foo since it inherits from B");
#20
1
How about this solution?
该解决方案如何?
#include <type_traits>
template <typename U, typename = void> struct hasToString : std::false_type { };
template <typename U>
struct hasToString<U,
typename std::enable_if<bool(sizeof(&U::toString))>::type
> : std::true_type { };
#21
1
Here is my version that handles all possible member function overloads with arbitrary arity, including template member functions, possibly with default arguments. It distinguishes 3 mutually exclusive scenarios when making a member function call to some class type, with given arg types: (1) valid, or (2) ambiguous, or (3) non-viable. Example usage:
下面是我的版本,它处理所有可能的成员函数重载,具有任意的特性,包括模板成员函数,可能使用默认参数。它区分了3个相互排斥的场景,当对某个类类型进行成员函数调用时,给定的arg类型:(1)有效,或(2)不明确,或(3)不可行。使用示例:
#include <string>
#include <vector>
HAS_MEM(bar)
HAS_MEM_FUN_CALL(bar)
struct test
{
void bar(int);
void bar(double);
void bar(int,double);
template < typename T >
typename std::enable_if< not std::is_integral<T>::value >::type
bar(const T&, int=0){}
template < typename T >
typename std::enable_if< std::is_integral<T>::value >::type
bar(const std::vector<T>&, T*){}
template < typename T >
int bar(const std::string&, int){}
};
Now you can use it like this:
现在你可以这样使用它:
int main(int argc, const char * argv[])
{
static_assert( has_mem_bar<test>::value , "");
static_assert( has_valid_mem_fun_call_bar<test(char const*,long)>::value , "");
static_assert( has_valid_mem_fun_call_bar<test(std::string&,long)>::value , "");
static_assert( has_valid_mem_fun_call_bar<test(std::vector<int>, int*)>::value , "");
static_assert( has_no_viable_mem_fun_call_bar<test(std::vector<double>, double*)>::value , "");
static_assert( has_valid_mem_fun_call_bar<test(int)>::value , "");
static_assert( std::is_same<void,result_of_mem_fun_call_bar<test(int)>::type>::value , "");
static_assert( has_valid_mem_fun_call_bar<test(int,double)>::value , "");
static_assert( not has_valid_mem_fun_call_bar<test(int,double,int)>::value , "");
static_assert( not has_ambiguous_mem_fun_call_bar<test(double)>::value , "");
static_assert( has_ambiguous_mem_fun_call_bar<test(unsigned)>::value , "");
static_assert( has_viable_mem_fun_call_bar<test(unsigned)>::value , "");
static_assert( has_viable_mem_fun_call_bar<test(int)>::value , "");
static_assert( has_no_viable_mem_fun_call_bar<test(void)>::value , "");
return 0;
}
Here is the code, written in c++11, however, you can easily port it (with minor tweaks) to non-c++11 that has typeof extensions (e.g. gcc). You can replace the HAS_MEM macro with your own.
这里是用c++11编写的代码,但是,您可以很容易地将它移植到具有扩展类型(例如gcc)的非c++11。您可以用自己的HAS_MEM宏替换HAS_MEM宏。
#pragma once
#if __cplusplus >= 201103
#include <utility>
#include <type_traits>
#define HAS_MEM(mem) \
\
template < typename T > \
struct has_mem_##mem \
{ \
struct yes {}; \
struct no {}; \
\
struct ambiguate_seed { char mem; }; \
template < typename U > struct ambiguate : U, ambiguate_seed {}; \
\
template < typename U, typename = decltype(&U::mem) > static constexpr no test(int); \
template < typename > static constexpr yes test(...); \
\
static bool constexpr value = std::is_same<decltype(test< ambiguate<T> >(0)),yes>::value ; \
typedef std::integral_constant<bool,value> type; \
};
#define HAS_MEM_FUN_CALL(memfun) \
\
template < typename Signature > \
struct has_valid_mem_fun_call_##memfun; \
\
template < typename T, typename... Args > \
struct has_valid_mem_fun_call_##memfun< T(Args...) > \
{ \
struct yes {}; \
struct no {}; \
\
template < typename U, bool = has_mem_##memfun<U>::value > \
struct impl \
{ \
template < typename V, typename = decltype(std::declval<V>().memfun(std::declval<Args>()...)) > \
struct test_result { using type = yes; }; \
\
template < typename V > static constexpr typename test_result<V>::type test(int); \
template < typename > static constexpr no test(...); \
\
static constexpr bool value = std::is_same<decltype(test<U>(0)),yes>::value; \
using type = std::integral_constant<bool, value>; \
}; \
\
template < typename U > \
struct impl<U,false> : std::false_type {}; \
\
static constexpr bool value = impl<T>::value; \
using type = std::integral_constant<bool, value>; \
}; \
\
template < typename Signature > \
struct has_ambiguous_mem_fun_call_##memfun; \
\
template < typename T, typename... Args > \
struct has_ambiguous_mem_fun_call_##memfun< T(Args...) > \
{ \
struct ambiguate_seed { void memfun(...); }; \
\
template < class U, bool = has_mem_##memfun<U>::value > \
struct ambiguate : U, ambiguate_seed \
{ \
using ambiguate_seed::memfun; \
using U::memfun; \
}; \
\
template < class U > \
struct ambiguate<U,false> : ambiguate_seed {}; \
\
static constexpr bool value = not has_valid_mem_fun_call_##memfun< ambiguate<T>(Args...) >::value; \
using type = std::integral_constant<bool, value>; \
}; \
\
template < typename Signature > \
struct has_viable_mem_fun_call_##memfun; \
\
template < typename T, typename... Args > \
struct has_viable_mem_fun_call_##memfun< T(Args...) > \
{ \
static constexpr bool value = has_valid_mem_fun_call_##memfun<T(Args...)>::value \
or has_ambiguous_mem_fun_call_##memfun<T(Args...)>::value; \
using type = std::integral_constant<bool, value>; \
}; \
\
template < typename Signature > \
struct has_no_viable_mem_fun_call_##memfun; \
\
template < typename T, typename... Args > \
struct has_no_viable_mem_fun_call_##memfun < T(Args...) > \
{ \
static constexpr bool value = not has_viable_mem_fun_call_##memfun<T(Args...)>::value; \
using type = std::integral_constant<bool, value>; \
}; \
\
template < typename Signature > \
struct result_of_mem_fun_call_##memfun; \
\
template < typename T, typename... Args > \
struct result_of_mem_fun_call_##memfun< T(Args...) > \
{ \
using type = decltype(std::declval<T>().memfun(std::declval<Args>()...)); \
};
#endif
#22
1
You can skip all the metaprogramming in C++14, and just write this using fit::conditional
from the Fit library:
您可以跳过c++ 14中的所有元编程,只需使用fit::条件句从fit库:
template<class T>
std::string optionalToString(T* x)
{
return fit::conditional(
[](auto* obj) -> decltype(obj->toString()) { return obj->toString(); },
[](auto*) { return "toString not defined"; }
)(x);
}
You can also create the function directly from the lambdas as well:
您也可以直接从lambdas创建函数:
FIT_STATIC_LAMBDA_FUNCTION(optionalToString) = fit::conditional(
[](auto* obj) -> decltype(obj->toString(), std::string()) { return obj->toString(); },
[](auto*) -> std::string { return "toString not defined"; }
);
However, if you are using a compiler that doesn't support generic lambdas, you will have to write separate function objects:
但是,如果您使用的编译器不支持泛型lambdas,则必须编写独立的函数对象:
struct withToString
{
template<class T>
auto operator()(T* obj) const -> decltype(obj->toString(), std::string())
{
return obj->toString();
}
};
struct withoutToString
{
template<class T>
std::string operator()(T*) const
{
return "toString not defined";
}
};
FIT_STATIC_FUNCTION(optionalToString) = fit::conditional(
withToString(),
withoutToString()
);
#23
0
Here is an example of the working code.
下面是一个工作代码的示例。
template<typename T>
using toStringFn = decltype(std::declval<const T>().toString());
template <class T, toStringFn<T>* = nullptr>
std::string optionalToString(const T* obj, int)
{
return obj->toString();
}
template <class T>
std::string optionalToString(const T* obj, long)
{
return "toString not defined";
}
int main()
{
A* a;
B* b;
std::cout << optionalToString(a, 0) << std::endl; // This is A
std::cout << optionalToString(b, 0) << std::endl; // toString not defined
}
toStringFn<T>* = nullptr
will enable the function which takes extra int
argument which has a priority over function which takes long
when called with 0
.
toStringFn
You can use the same principle for the functions which returns true
if function is implemented.
如果实现了函数,则可以对返回true的函数使用相同的原则。
template <typename T>
constexpr bool toStringExists(long)
{
return false;
}
template <typename T, toStringFn<T>* = nullptr>
constexpr bool toStringExists(int)
{
return true;
}
int main()
{
A* a;
B* b;
std::cout << toStringExists<A>(0) << std::endl; // true
std::cout << toStringExists<B>(0) << std::endl; // false
}