如何检查模板类是否具有成员函数?
是否可以编写一个模板,根据类上是否定义了某个成员函数来改变行为?
这是我想要编写的一个简单示例:
template<class T>
std::string optionalToString(T* obj)
{
if (FUNCTION_EXISTS(T->toString))
return obj->toString();
else
return "toString not defined";
}
因此,如果 class T
定义了 toString()
,则它会使用它; 否则,它不会。 我不知道该怎么做的神奇部分是“FUNCTION_EXISTS”部分。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(30)
是的,使用 SFINAE,您可以检查给定的类是否提供了某种方法。 这是工作代码:
我刚刚使用 Linux 和 gcc 4.1/4.3 对其进行了测试。 我不知道它是否可以移植到运行不同编译器的其他平台。
Yes, with SFINAE you can check if a given class does provide a certain method. Here's the working code:
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.
这个问题很老了,但在 C++11 中,我们有了一种新方法来检查函数是否存在(或者是否存在任何非类型成员,真的),再次依赖 SFINAE:
现在进行一些解释。 首先,我使用 表达式 SFINAE如果
decltype
内的第一个表达式无效(即该函数不存在),则从重载解析中排除serialize(_imp)
函数。void()
用于使所有这些函数的返回类型为void
。0
参数用于优先选择os << obj
重载(如果两者都可用)(文字0
的类型为int
,因此第一个重载是更好的匹配)。现在,您可能需要一个特征来检查函数是否存在。 幸运的是,写起来很容易。 但请注意,您需要为您可能想要的每个不同的函数名称编写一个特征您自己。
实例。
然后进行解释。 首先, sfinae_true 是一个辅助类型,它基本上与编写 decltype(void(std::declval().stream(a0)), std:: true_type{})。 优点很简单,就是更短。
接下来,
struct has_stream : decltype(...)
最后继承自std::true_type
或std::false_type
,具体取决于关于test_stream
中的decltype
检查是否失败。最后,std::declval 为您提供传递的任何类型的“值”,而无需知道如何构造它。 请注意,这仅在未评估的上下文中才可能,例如 decltype、sizeof 等。
请注意,
decltype
不一定需要,因为sizeof
(以及所有未评估的上下文)获得了该增强。 只是 decltype 已经提供了一个类型,因此更加干净。 下面是其中一个重载的sizeof
版本:出于同样的原因,
int
和long
参数仍然存在。 数组指针用于提供可以使用sizeof
的上下文。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:
Now onto some explanations. First thing, I use expression SFINAE to exclude the
serialize(_imp)
functions from overload resolution, if the first expression insidedecltype
isn't valid (aka, the function doesn't exist).The
void()
is used to make the return type of all those functionsvoid
.The
0
argument is used to prefer theos << obj
overload if both are available (literal0
is of typeint
and as such the first overload is a better match).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.
Live example.
And on to explanations. First,
sfinae_true
is a helper type, and it basically amounts to the same as writingdecltype(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 eitherstd::true_type
orstd::false_type
in the end, depending on whether thedecltype
check intest_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 asdecltype
,sizeof
and others.Note that
decltype
is not necessarily needed, assizeof
(and all unevaluated contexts) got that enhancement. It's just thatdecltype
already delivers a type and as such is just cleaner. Here's asizeof
version of one of the overloads:The
int
andlong
parameters are still there for the same reason. The array pointer is used to provide a context wheresizeof
can be used.C++20 -
requires
表达式C++20 带来了概念和各种工具,例如
需要
表达式,这是检查函数是否存在的内置方法。 使用它们,您可以重写您的optionalToString
函数,如下所示:Pre-C++20 - 检测工具包
N4502 提出了一个检测工具包,用于包含到 C++17 标准库中,最终将其纳入库基础 TS v2 中。 它很可能永远不会进入标准,因为它已被
requires
表达式所包含,但它仍然以某种优雅的方式解决了问题。 该工具包引入了一些元函数,包括std::is_detected
可用于在其顶部轻松编写类型或函数检测元函数。 以下是如何使用它:请注意,上面的示例未经测试。 标准库中尚未提供检测工具包,但该提案包含完整的实现,如果您确实需要,可以轻松复制。 它与 C++17 功能
if constexpr
配合得很好:C++14 - Boost.Hana
Boost.Hana 显然是建立在这个特定示例的基础上的,并在其文档中提供了 C++14 的解决方案,所以我直接引用它:
Boost.TTI
执行此类检查的另一个有点惯用的工具包 - 尽管不太优雅 - 是 Boost.TTI,在 Boost 1.54.0 中引入。 对于您的示例,您必须使用宏
BOOST_TTI_HAS_MEMBER_FUNCTION
。 以下是如何使用它:然后,您可以使用
bool
创建 SFINAE 检查。解释
宏
BOOST_TTI_HAS_MEMBER_FUNCTION
生成元函数has_member_function_toString
,它将检查的类型作为其第一个模板参数。 第二个模板参数对应于成员函数的返回类型,后面的参数对应于函数参数的类型。 如果类T
具有成员函数std::string toString()
,则成员value
包含true
。或者,has_member_function_toString 可以将成员函数指针作为模板参数。 因此,可以将
has_member_function_toString::value
替换为has_member_function_toString::value
>。C++20 -
requires
expressionsWith C++20 come concepts and assorted tools such as
requires
expressions which are a built-in way to check for a function existence. With them you could rewrite youroptionalToString
function as follows:Pre-C++20 - Detection toolkit
N4502 proposes a detection toolkit for inclusion into the C++17 standard library that eventually made it into the library fundamentals TS v2. It most likely won't ever get into the standard because it has been subsumed by
requires
expressions since, but it still solves the problem in a somewhat elegant manner. The toolkit introduces some metafunctions, includingstd::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: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
:C++14 - Boost.Hana
Boost.Hana apparently builds upon this specific example and provides a solution for C++14 in its documentation, so I'm going to quote it directly:
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:Then, you could use the
bool
to create a SFINAE check.Explanation
The macro
BOOST_TTI_HAS_MEMBER_FUNCTION
generates the metafunctionhas_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 membervalue
containstrue
if the classT
has a member functionstd::string toString()
.Alternatively,
has_member_function_toString
can take a member function pointer as a template parameter. Therefore, it is possible to replacehas_member_function_toString<T, std::string>::value
byhas_member_function_toString<std::string T::* ()>::value
.C++ 允许使用 SFINAE 来实现此目的(请注意,使用 C++11 功能,这会更简单,因为它支持几乎任意表达式上的扩展 SFINAE - 下面的内容是为了与常见的 C++03 编译器一起工作而设计的):
上面的模板和宏尝试实例化模板,为其提供成员函数指针类型和实际的成员函数指针。 如果类型不适合,SFINAE 会导致模板被忽略。 用法如下:
但请注意,您不能只在
if
分支中调用toString
函数。 由于编译器将检查两个分支的有效性,因此在函数不存在的情况下会失败。 一种方法是再次使用 SFINAE(enable_if
也可以从 boost 获得):享受使用它的乐趣。 它的优点是它也适用于重载成员函数,也适用于 const 成员函数(记住使用 std::string(T::*)() const )作为成员函数指针类型!)。
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):
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 do not fit, SFINAE causes the template to be ignored. Usage like this:
But note that you cannot just call that
toString
function in thatif
branch. Since the compiler will check for validity in both branches, that would fail for cases the function doesn't exist. One way is to use SFINAE once again (enable_if
can be obtained from boost, too):Have fun using it. The advantage of it is that it also works for overloaded member functions, and also for
const
member functions (remember usingstd::string(T::*)() const
as the member function pointer type then!).虽然这个问题已经有两年了,但我还是敢于补充我的答案。 希望它能够澄清之前无可争议的优秀解决方案。 我采用了 Nicola Bonelli 和 Johannes Schaub 非常有用的答案,并将它们合并到一个解决方案中,恕我直言,该解决方案更具可读性、清晰性,并且不需要
typeof
扩展:我用 gcc 4.1.2 检查了它。
这主要归功于 Nicola Bonelli 和 Johannes Schaub,所以如果我的回答对您有帮助,请给他们投票:)
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: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 :)
C++11 的一个简单解决方案:
更新,3 年后:(且未经测试)。 为了测试是否存在,我认为这会起作用:
A simple solution for C++11:
Update, 3 years later: (and this is untested). To test for the existence, I think this will work:
好吧,这个问题已经有一长串答案了,但我想强调 Morwenn 的评论:有一个针对 C++17 的提案,使它变得非常简单。 请参阅 N4502 了解详细信息,但作为一个独立的示例,请考虑以下内容。
这部分是常量部分,将其放在标题中。
然后是变量部分,您可以在其中指定要查找的内容(类型、成员类型、函数、成员函数等)。 对于OP:
以下示例取自 N4502,显示了更精细的探测:
与上述其他实现相比,这个实现相当简单:一组精简的工具(< code>void_t 和
detect
)就足够了,不需要毛茸茸的宏。 此外,还报告了(参见N4502),它比以前的方法明显更高效(编译时间和编译器内存消耗)。这是一个实例。 它可以与 Clang 配合使用,但不幸的是,5.1 之前的 GCC 版本遵循对 C++11 标准的不同解释,导致
void_t
无法按预期工作。 Yakk 已经提供了解决方法:使用以下void_t
定义 (参数列表中的void_t可以工作,但不能作为返回类型):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.
This part is the constant part, put it in a header.
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:
The following example, taken from N4502, shows a more elaborate probe:
Compared to the other implementations described above, this one is fairly simple: a reduced set of tools (
void_t
anddetect
) 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.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 ofvoid_t
(void_t in parameter list works but not as return type):这就是类型特征的用途。 不幸的是,它们必须手动定义。 对于您的情况,请想象以下情况:
This is what type traits are there for. Unfortunately, they have to be defined manually. In your case, imagine the following:
在 C++17 中实现此目的的另一种方法(受
boost:hana
启发)。此解决方案不需要
has_something
SFINAE 类型特征类。溶液
测试
Yet another way to do it in C++17 (inspired by
boost:hana
).This solution does not require
has_something<T>
SFINAE type traits classes.Solution
Test
这是我在 C++20 中找到的最简洁的方法,与您的问题非常接近:
See it live on godbolt: https://gcc.godbolt.org/z/5jb1d93Ms
Here is the most concise way I found in C++20, which is very close from your question:
See it live on godbolt: https://gcc.godbolt.org/z/5jb1d93Ms
这是针对“如果我做了 X,它会编译吗?”这一一般问题的 C++11 解决方案。
特征
has_to_string
使得has_to_string::value
为true
当且仅当T
有一个方法 < code>.toString 可以在此上下文中使用 0 个参数进行调用。接下来,我将使用标签调度:
这往往比复杂的 SFINAE 表达式更易于维护。
如果您发现自己经常这样做,则可以使用宏编写这些特征,但它们相对简单(每行几行),因此可能不值得:
上面的作用是创建一个宏
MAKE_CODE_TRAIT
。 您向其传递所需特征的名称,以及一些可以测试类型T
的代码。 因此:创建了上述特征类。
顺便说一句,上述技术是 MS 所谓的“表达式 SFINAE”的一部分,他们的 2013 编译器很难失败。
请注意,在 C++1y 中,可以使用以下语法:
这是滥用大量 C++ 功能的内联编译条件分支。 这样做可能不值得,因为(内联代码)的好处不值得(几乎没有人理解它是如何工作的)成本,但上述解决方案的存在可能会令人感兴趣。
This is a C++11 solution for the general problem if "If I did X, would it compile?"
Trait
has_to_string
such thathas_to_string<T>::value
istrue
if and only ifT
has a method.toString
that can be invoked with 0 arguments in this context.Next, I'd use tag dispatching:
which tends to be more maintainable than complex SFINAE expressions.
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:
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 typeT
. Thus: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.
Note that in C++1y the following syntax is possible:
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.
以下是一些使用片段:
*所有这一切的核心是
检查给定类中的成员
x
。 可以是 var、func、class、union 或 enum:检查成员函数
void x()
:检查成员变量
x
:检查成员类
x
:检查成员联合
x
:检查成员枚举
x
:检查任何成员函数
x
,无论签名如何:或
详细信息和核心:
宏(El Diablo!):
CREATE_MEMBER_CHECK:
CREATE_MEMBER_VAR_CHECK:
CREATE_MEMBER_FUNC_SIG_CHECK:
CREATE_MEMBER_CLASS_CHECK:
CREATE_MEMBER_UNION_CHECK
:
CREATE_MEMBER_ENUM_CHECK: CREATE_MEMBER_FUNC_CHECK:
CREATE_MEMBER_CHECKS:
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:Check for member function
void x()
:Check for member variable
x
:Check for member class
x
:Check for member union
x
:Check for member enum
x
:Check for any member function
x
regardless of signature:OR
Details and core:
Macros (El Diablo!):
CREATE_MEMBER_CHECK:
CREATE_MEMBER_VAR_CHECK:
CREATE_MEMBER_FUNC_SIG_CHECK:
CREATE_MEMBER_CLASS_CHECK:
CREATE_MEMBER_UNION_CHECK:
CREATE_MEMBER_ENUM_CHECK:
CREATE_MEMBER_FUNC_CHECK:
CREATE_MEMBER_CHECKS:
使用 C++ 20,您可以编写以下内容:
With C++ 20 you can write the following:
我在另一个线程中写了一个答案,该线程(与上面的解决方案不同)还检查继承的成员函数:
SFINAE 检查继承的成员函数
以下是该解决方案的一些示例:
示例 1:
我们正在检查具有以下签名的成员:
T::const_iterator begin() const
请注意,它甚至检查方法的常量性,并且也适用于原始类型。 (我的意思是
has_const_begin::value
为 false,不会导致编译时错误。)示例 2
现在我们正在寻找签名:
void foo(MyClass&, unsigned)
请注意,MyClass 不必是默认可构造的或满足任何特殊概念。 该技术也适用于模板成员。
我热切地等待对此的意见。
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
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.)Example 2
Now we are looking for the signature:
void foo(MyClass&, unsigned)
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.
I am eagerly waiting opinions regarding this.
如果该方法恰好在基类中定义,则 litb 在此提供的标准 C++ 解决方案将无法按预期工作。
有关处理这种情况的解决方案,请参阅:
俄语:
http://www.rsdn.ru/forum/message/2759773.1.aspx
Roman.Perepelitsa 的英文翻译:
http://groups .google.com/group/comp.lang.c++.moderated/tree/browse_frm/thread/4f7c7a96f9afbe44/c95a7b4c645e449f?pli=1
这太聪明了。 然而,此解决方案的一个问题是,如果正在测试的类型不能用作基类(例如原始类型),则会出现编译器错误。
在 Visual Studio 中,我注意到,如果使用没有参数的方法,则需要一对额外的需要在 sizeof 表达式中的参数周围插入冗余 ( ) 来 ductuce( ) 。
The standard C++ solution presented here by litb will not work as expected if the method happens to be defined in a base class.
For a solution that handles this situation refer to :
In Russian :
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
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.
这是一个很好的小谜题 - 很好的问题!
这是 Nicola Bonelli 的解决方案 不依赖于非标准
typeof
运算符。不幸的是,它不适用于 GCC (MinGW) 3.4.5 或 Digital Mars 8.42n,但它适用于所有版本的 MSVC(包括 VC6)和 Comeau C++。
较长的注释块包含有关其工作原理(或应该工作)的详细信息。 正如它所说,我不确定哪种行为符合标准 - 我欢迎对此发表评论。
更新 - 2008 年 11 月 7 日:
看起来虽然此代码在语法上是正确的,但 MSVC 和 Comeau C++ 显示的行为不遵循标准(感谢 Leon Timmermans 和 litb 为我指明了正确的方向)。 C++03 标准规定如下:
因此,看起来当 MSVC 或 Comeau 考虑
T
的toString()
成员函数在doToString()
当模板被实例化时,这是不正确的(即使这实际上是我在这种情况下寻找的行为)。GCC 和 Digital Mars 的行为看起来是正确的 - 在这两种情况下,非成员
toString()
函数都绑定到调用。老鼠 - 我以为我可能找到了一个聪明的解决方案,但我发现了几个编译器错误......
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.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++.
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:
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:
So, it looks like that when MSVC or Comeau consider the
toString()
member function ofT
performing name lookup at the call site indoToString()
when the template is instantiated, that is incorrect (even though it's actually the behavior I was looking for in this case).The behavior of GCC and Digital Mars looks to be correct - in both cases the non-member
toString()
function is bound to the call.Rats - I thought I might have found a clever solution, instead I uncovered a couple compiler bugs...
通过编写
Has_foo
概念检查,使用 SFINAE 和模板部分特化的示例:An example using SFINAE and template partial specialization, by writing a
Has_foo
concept check:MSVC 具有 __if_exists 和 __if_not_exists 关键字(Doc) 。 与 Nicola 的 typeof-SFINAE 方法一起,我可以像 OP 所寻找的那样创建对 GCC 和 MSVC 的检查。
更新:源代码可以在此处
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.
Update: Source can be found Here
我修改了 https://stackoverflow.com/a/264088/2712152 中提供的解决方案,使其更加通用。 此外,由于它不使用任何新的 C++11 功能,我们可以将它与旧编译器一起使用,并且还应该与 msvc 一起使用。 但编译器应该允许 C99 使用它,因为它使用可变宏。
以下宏可用于检查特定类是否具有特定的 typedef。
以下宏可用于检查特定类是否具有特定成员函数以及任何给定数量的参数。
我们可以使用上面的 2 个宏来执行 has_typedef 和 has_mem_func 的检查,如下所示:
I modified the solution provided in https://stackoverflow.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.
The following macro can be used to check if a particular class has a particular typedef or not.
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.
We can use the above 2 macros to perform the checks for has_typedef and has_mem_func as:
我知道这个问题已经有很多年了,但我认为对于像我这样的人来说,拥有一个更完整的更新答案会很有用,该答案也适用于
const
重载方法,例如std::vector
>::开始
。基于该答案和从我的后续问题中回答,这里有一个更完整的答案。 请注意,这仅适用于 C++11 及更高版本。
或更短的版本:
请注意,此处必须提供完整的示例调用。 这意味着,如果我们测试
resize
方法是否存在,那么我们就会放置resize(0)
。深度魔术解释:
这个问题的第一个答案使用了
test( decltype(&C::helloworld) )
; 然而,当正在测试的方法由于 const 重载而不明确时,就会出现问题,从而导致替换尝试失败。为了解决这个歧义,我们使用一个 void 语句,它可以接受任何参数,因为它总是被翻译成一个
noop
,因此歧义被消除,并且只要该方法存在,调用就有效:这是发生的事情为了:() 创建一个可调用值,然后可以调用该值。 之后,
我们使用 std::declval
begin
的值作为参数传递给 void 语句。 然后,我们使用内置的 decltype 检索该 void 表达式的类型,以便它可以用作模板类型参数。 如果begin
不存在,则替换无效,并且根据 SFINAE,将使用其他声明。I know that this question is years old, but I think it would useful for people like me to have a more complete updated answer that also works for
const
overloaded methods such asstd::vector<>::begin
.Based on that answer and that answer from my follow up question, here's a more complete answer. Note that this will only work with C++11 and higher.
Or the shorter version:
Note that here a complete sample call must be provided. This means that if we tested for the
resize
method's existence then we would have putresize(0)
.Deep magic explanation:
The first answer posted of this question used
test( decltype(&C::helloworld) )
; however this is problematic when the method it is testing is ambiguous due const overloading, thus making the substitution attempt fail.To solve this ambiguity we use a void statement which can take any parameters because it is always translated into a
noop
and thus the ambiguity is nullified and the call is valid as long as the method exists:Here's what's happening in order:
We use
std::declval<T &>()
to create a callable value for whichbegin
can then be called. After that the value ofbegin
is passed as a parameter to a void statement. We then retrieve the type of that void expression using the builtindecltype
so that it can be used as a template type argument. Ifbegin
doesn't exist then the substitution is invalid and as per SFINAE the other declaration is used instead.奇怪的是,没有人提出我在这个网站上看到过的以下好技巧:
你必须确保 T 是一个类。 看起来 foo 查找中的歧义是替换失败。 我让它在 gcc 上工作,但不确定它是否是标准的。
Strange nobody suggested the following nice trick I saw once on this very site :
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.
这个解决方案怎么样?
How about this solution?
可用于检查类型是否支持某些“功能”的通用模板:
检查是否存在与签名
double(const char* 兼容) 的方法
foo
的模板)示例
http://coliru.stacked-crooked.com/a/83c6a631ed42cea4
The generic template that can be used for checking if some "feature" is supported by the type:
The template that checks whether there is a method
foo
that is compatible with signaturedouble(const char*)
Examples
http://coliru.stacked-crooked.com/a/83c6a631ed42cea4
我的看法:普遍确定某事物是否可调用,而无需为每个事物创建详细的类型特征,或者使用实验性功能或长代码:
用法:
My take: to universally determine if something is callable without making verbose type traits for each and every one, or using experimental features, or long code:
Usage:
这里有很多答案,但我未能找到一个执行真实方法解析排序的版本,同时不使用任何较新的 c++ 功能(仅使用 c++98 功能)。
注意:这个版本已经过测试,可以与 vc++2013、g++ 5.2.0 和在线编译器一起使用。
所以我想出了一个仅使用 sizeof() 的版本:
现场演示(具有扩展的返回类型检查和vc++2010 解决方法): http://cpp.sh/5b2vs
没有来源,因为我想出了我自己。
在 g++ 编译器上运行 Live demo 时,请注意允许数组大小为 0,这意味着使用的 static_assert 即使失败也不会触发编译器错误。
常用的解决方法是将宏中的“typedef”替换为“extern”。
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.
So I came up with a version, that only uses sizeof():
Live demo (with extended return type checking and vc++2010 workaround): 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'.
这是我的版本,它处理任意数量的所有可能的成员函数重载,包括模板成员函数,可能带有默认参数。 当使用给定的 arg 类型对某些类类型进行成员函数调用时,它区分 3 种互斥的情况:(1) 有效,或 (2) 不明确,或 (3) 不可行。 示例用法:
现在您可以像这样使用它:
这是用 c++11 编写的代码,但是,您可以轻松地将其(只需稍作调整)移植到具有 typeof 扩展(例如 gcc)的非 c++11 。 您可以将 HAS_MEM 宏替换为您自己的宏。
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:
Now you can use it like this:
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++14 中的所有元编程,只需使用
fit::conditional
来自 Fit 库:您也可以直接从 lambda 创建函数:
但是,如果您使用的编译器不支持泛型 lambda,则必须编写单独的函数对象:
You can skip all the metaprogramming in C++14, and just write this using
fit::conditional
from the Fit library:You can also create the function directly from the lambdas as well:
However, if you are using a compiler that doesn't support generic lambdas, you will have to write separate function objects:
可能不如其他示例好,但这就是我为 C++11 想到的。 这适用于选择重载方法。
用法示例
Probably not as good as other examples, but this is what I came up with for C++11. This works for picking overloaded methods.
Example usage
C++03方式
使用上面的宏,你可以找到类中任何成员的存在,无论是变量还是方法。 如果有两个同名的方法,那么我们还必须提供方法的类型。
用法:
如果
S
中存在 2 个具有相同名称Foo
的方法,则第二个cout
可能会打印 0。 对于成员变量,类型(如第 3 个cout
中所述)是多余的,因为变量名称只能为 1。但是,要检查特定类型,它很有用(对于方法和变量)。C++03 way
Using above macro, you can find any member's existence in a class, be it variable or method. If there are 2 methods with same name, then we also have to provide the type of the method.
Usage:
The 2nd
cout
may print 0 if there are 2 methods with same nameFoo
present inS
. In case of member variable, the type (as mentioned in 3rdcout
) is redundant as the variable name can be only 1. However to check the specific type, it's useful (for both method and variable).这是工作代码的示例。
toStringFn* = nullptr
将启用需要额外int
参数的函数,该函数的优先级高于使用long
调用时需要long
的函数代码>0。如果函数被实现,您可以对返回
true
的函数使用相同的原则。Here is an example of the working code.
toStringFn<T>* = nullptr
will enable the function which takes extraint
argument which has a priority over function which takeslong
when called with0
.You can use the same principle for the functions which returns
true
if function is implemented.