SFINAE 有哪些好的用途?
我想了解更多模板元编程。 我知道 SFINAE 代表“替换失败不是错误”。 但有人可以告诉我 SFINAE 的好用处吗?
I want to get into more template meta-programming. I know that SFINAE stands for "substitution failure is not an error." But can someone show me a good use for SFINAE?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
我喜欢使用
SFINAE
来检查布尔条件。它可能非常有用。 例如,我用它来检查使用运算符逗号收集的初始值设定项列表是否不超过固定大小。
仅当 M 小于或等于 N 时才接受列表,这意味着初始值设定项列表没有太多元素。
语法
char(*)[C]
的意思是:指向元素类型为 char、大小为C
的数组的指针。 如果C
为 false(此处为 0),则我们得到无效类型char(*)[0]
,指向零大小数组的指针:SFINAE 使得模板将被忽略。用
boost::enable_if
表示,看起来像这样在实践中,我经常发现检查条件的能力是一个有用的能力。
I like using
SFINAE
to check boolean conditions.It can be quite useful. For example, i used it to check whether an initializer list collected using operator comma is no longer than a fixed size
The list is only accepted when M is smaller than or equal to N, which means that the initializer list has not too many elements.
The syntax
char(*)[C]
means: Pointer to an array with element type char and sizeC
. IfC
is false (0 here), then we get the invalid typechar(*)[0]
, pointer to a zero sized array: SFINAE makes it so that the template will be ignored then.Expressed with
boost::enable_if
, that looks like thisIn practice, i often find the ability to check conditions a useful ability.
下面是一个示例(来自此处):
当
IsClassT< ;int>::Yes
被求值,0 不能转换为int int::*
因为 int 不是一个类,所以它不能有成员指针。 如果 SFINAE 不存在,那么您将收到编译器错误,例如“0 无法转换为非类类型 int 的成员指针”。 相反,它只使用...
形式返回 Two,因此计算结果为 false,int 不是类类型。Heres one example (from here):
When
IsClassT<int>::Yes
is evaluated, 0 cannot be converted toint int::*
because int is not a class, so it can't have a member pointer. If SFINAE didn't exist, then you would get a compiler error, something like '0 cannot be converted to member pointer for non-class type int'. Instead, it just uses the...
form which returns Two, and thus evaluates to false, int is not a class type.在 C++11 中,SFINAE 测试变得更加漂亮。 以下是一些常见用法的示例:
根据特征选择函数重载
使用所谓的类型接收器习惯用法,您可以对类型进行相当任意的测试,例如检查它是否有成员以及该成员是否属于某种类型
这里是一个实例:http://ideone.com/dHhyHE
我最近还在我的博客中写了关于 SFINAE 和标签调度的整个部分(无耻的插件但相关)
从 C++14 开始,有一个 std:: void_t 本质上与我的 TypeSink 相同。
In C++11 SFINAE tests have become much prettier. Here are a few examples of common uses:
Pick a function overload depending on traits
Using a so called type sink idiom you can do pretty arbitrary tests on a type like checking if it has a member and if that member is of a certain type
Here is a live example: http://ideone.com/dHhyHE
I also recently wrote a whole section on SFINAE and tag dispatch in my blog (shameless plug but relevant) http://metaporky.blogspot.de/2014/08/part-7-static-dispatch-function.html
Note as of C++14 there is a std::void_t which is essentially the same as my TypeSink here.
Boost 的 enable_if 库为使用 SFINAE 提供了一个漂亮干净的界面。 我最喜欢的用法示例之一位于 Boost.Iterator< /a> 库。 SFINAE 用于启用迭代器类型转换。
Boost's enable_if library offers a nice clean interface for using SFINAE. One of my favorite usage examples is in the Boost.Iterator library. SFINAE is used to enable iterator type conversions.
在我看来,其他答案提供的示例比需要的更复杂。
这是 cppreference 中稍微容易理解的示例:
输出:
如您所见,在第三次测试调用中,替换失败且没有错误。
Examples provided by other answers seems to me more complicated than needed.
Here is the slightly easier to understand example from cppreference :
Output:
As you can see, in the third call of test, substitution fails without errors.
这是另一个(最新的)SFINAE 示例,基于 Greg Rogers 的答案:
这样,您可以检查
value
的值来查看T
是否是一个类:Here's another (late) SFINAE example, based on Greg Rogers's answer:
In this way, you can check the
value
's value to see whetherT
is a class or not:这是 SFINAE 的一篇好文章: C++ 的 SFINAE 概念简介:编译时自省类成员的。
总结如下:
declval
是一个实用程序,它为您提供对无法轻松构造的类型的对象的“虚假引用”。declval
对于我们的 SFINAE 构造来说非常方便。Here is one good article of SFINAE: An introduction to C++'s SFINAE concept: compile-time introspection of a class member.
Summary it as following:
declval
is an utility that gives you a "fake reference" to an object of a type that couldn't be easily construct.declval
is really handy for our SFINAE constructions.以下代码使用 SFINAE 让编译器根据类型是否具有特定方法来选择重载:
输出:
The following code uses SFINAE to let compiler select an overload based on whether a type has certain method or not:
Output:
C++17 可能会提供一种查询功能的通用方法。 请参阅 N4502 了解详细信息,但作为一个独立的示例,请考虑以下内容。
这部分是常量部分,将其放在标题中。
以下示例取自 N4502 的支持显示了用法:
与其他实现相比,这个实现相当简单:一组精简的工具(
void_t
和检测
)就足够了。 此外,还报告了(参见N4502),它比以前的方法明显更高效(编译时间和编译器内存消耗)。这是一个 实时示例,其中包括 GCC pre 的可移植性调整5.1.
C++17 will probably provide a generic means to query for features. See N4502 for details, but as a self-contained example consider the following.
This part is the constant part, put it in a header.
The following example, taken from N4502, shows the usage:
Compared to the other implementations, this one is fairly simple: a reduced set of tools (
void_t
anddetect
) suffices. 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, which includes portability tweaks for GCC pre 5.1.
在这里,我使用模板函数重载(不是直接 SFINAE)来确定指针是函数还是成员类指针:(是否可以修复 iostream cout/cerr 成员函数指针打印为 1 或 true 的问题?)
https://godbolt.org/z/c2NmzR
打印
正如代码所示,它可以 (取决于编译器“好”将)生成对函数的运行时调用,该函数将返回 true 或 false。 如果您想强制 is_function_pointer(var) 计算编译类型(运行时不执行函数调用),您可以使用 constexpr 变量技巧
: C++ 标准,所有
constexpr
变量都保证在编译时进行计算 (在编译时计算 C 字符串的长度。这真的是 constexpr 吗?)。Here, I am using template function overloading (not directly SFINAE) to determine whether a pointer is a function or member class pointer: (Is possible to fix the iostream cout/cerr member function pointers being printed as 1 or true?)
https://godbolt.org/z/c2NmzR
Prints
As the code is, it could (depending on the compiler "good" will) generate a run time call to a function which will return true or false. If you would like to force the
is_function_pointer(var)
to evaluate at compile type (no function calls performed at run time), you can use theconstexpr
variable trick:By the C++ standard, all
constexpr
variables are guaranteed to be evaluated at compile time (Computing length of a C string at compile time. Is this really a constexpr?).